--- loncom/lond	2009/10/19 23:16:36	1.429
+++ loncom/lond	2012/02/28 15:54:07	1.486
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
 #
-# $Id: lond,v 1.429 2009/10/19 23:16:36 raeburn Exp $
+# $Id: lond,v 1.486 2012/02/28 15:54:07 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -15,6 +15,7 @@
 #
 # LON-CAPA is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
+
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
@@ -42,7 +43,6 @@ use Crypt::IDEA;
 use LWP::UserAgent();
 use Digest::MD5 qw(md5_hex);
 use GDBM_File;
-use Authen::Krb4;
 use Authen::Krb5;
 use localauth;
 use localenroll;
@@ -53,13 +53,14 @@ use LONCAPA::lonlocal;
 use LONCAPA::lonssl;
 use Fcntl qw(:flock);
 use Apache::lonnet;
+use Mail::Send;
 
 my $DEBUG = 0;		       # Non zero to enable debug log entries.
 
 my $status='';
 my $lastlog='';
 
-my $VERSION='$Revision: 1.429 $'; #' stupid emacs
+my $VERSION='$Revision: 1.486 $'; #' stupid emacs
 my $remoteVERSION;
 my $currenthostid="default";
 my $currentdomainid;
@@ -67,6 +68,9 @@ my $currentdomainid;
 my $client;
 my $clientip;			# IP address of client.
 my $clientname;			# LonCAPA name of client.
+my $clientversion;              # LonCAPA version running on client.
+my $clienthomedom;              # LonCAPA domain of homeID for client. 
+                                # primary library server. 
 
 my $server;
 
@@ -88,6 +92,8 @@ my %managers;			# Ip -> manager names
 
 my %perlvar;			# Will have the apache conf defined perl vars.
 
+my $dist;
+
 #
 #   The hash below is used for command dispatching, and is therefore keyed on the request keyword.
 #    Each element of the hash contains a reference to an array that contains:
@@ -417,8 +423,11 @@ sub ReadManagerTable {
 
    my $tablename = $perlvar{'lonTabDir'}."/managers.tab";
    if (!open (MANAGERS, $tablename)) {
-      logthis('<font color="red">No manager table.  Nobody can manage!!</font>');
-      return;
+       my $hostname = &Apache::lonnet::hostname($perlvar{'lonHostID'});
+       if (&Apache::lonnet::is_LC_dns($hostname)) {
+           &logthis('<font color="red">No manager table.  Nobody can manage!!</font>');
+       }
+       return;
    }
    while(my $host = <MANAGERS>) {
       chomp($host);
@@ -443,7 +452,7 @@ sub ReadManagerTable {
          }
       } else {
          logthis('<font color="green"> existing host'." $host</font>\n");
-         $managers{&Apache::lonnet::get_host_ip($host)} = $host;  # Use info from cluster tab if clumemeber
+         $managers{&Apache::lonnet::get_host_ip($host)} = $host;  # Use info from cluster tab if cluster memeber
       }
    }
 }
@@ -505,7 +514,8 @@ sub AdjustHostContents {
     my $me        = $perlvar{'lonHostID'};
 
     foreach my $line (split(/\n/,$contents)) {
-	if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/))) {
+	if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/) ||
+             ($line =~ /^\s*\^/))) {
 	    chomp($line);
 	    my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon)=split(/:/,$line);
 	    if ($id eq $me) {
@@ -593,11 +603,8 @@ sub InstallFile {
 #
 #   ConfigFileFromSelector: converts a configuration file selector
 #                 into a configuration file pathname.
-#                 It's probably no longer necessary to preserve
-#                 special handling of hosts or domain as those
-#                 files have been superceded by dns_hosts, dns_domain.
-#                 The default action is just to prepend the directory
-#                 and append .tab
+#                 Supports the following file selectors: 
+#                 hosts, domain, dns_hosts, dns_domain  
 #
 #
 #  Parameters:
@@ -610,15 +617,11 @@ sub ConfigFileFromSelector {
     my $tablefile;
 
     my $tabledir = $perlvar{'lonTabDir'}.'/';
-    if ($selector eq "hosts") {
-	$tablefile = $tabledir."hosts.tab";
-    } elsif ($selector eq "domain") {
-	$tablefile = $tabledir."domain.tab";
-    } else {
+    if (($selector eq "hosts") || ($selector eq "domain") || 
+        ($selector eq "dns_hosts") || ($selector eq "dns_domain")) {
 	$tablefile =  $tabledir.$selector.'.tab';
     }
     return $tablefile;
-
 }
 #
 #   PushFile:  Called to do an administrative push of a file.
@@ -644,6 +647,8 @@ sub PushFile {
     #  supported:
     #   hosts.tab  ($filename eq host).
     #   domain.tab ($filename eq domain).
+    #   dns_hosts.tab ($filename eq dns_host).
+    #   dns_domain.tab ($filename eq dns_domain). 
     # Construct the destination filename or reject the request.
     #
     # lonManage is supposed to ensure this, however this session could be
@@ -675,11 +680,31 @@ sub PushFile {
 	return "error:$!";
     } else {
 	&logthis('<font color="green"> Installed new '.$tablefile
-		 ."</font>");
-
+		 ." - transaction by: $clientname ($clientip)</font>");
+        my $adminmail = $perlvar{'lonAdmEMail'};
+        my $admindom = &Apache::lonnet::host_domain($perlvar{'lonHostID'});
+        if ($admindom ne '') {
+            my %domconfig =
+                &Apache::lonnet::get_dom('configuration',['contacts'],$admindom);
+            if (ref($domconfig{'contacts'}) eq 'HASH') {
+                if ($domconfig{'contacts'}{'adminemail'} ne '') {
+                    $adminmail = $domconfig{'contacts'}{'adminemail'};
+                }
+            }
+        }
+        if ($adminmail =~ /^[^\@]+\@[^\@]+$/) {
+            my $msg = new Mail::Send;
+            $msg->to($adminmail);
+            $msg->subject('LON-CAPA DNS update on '.$perlvar{'lonHostID'});
+            $msg->add('Content-type','text/plain; charset=UTF-8');
+            if (my $fh = $msg->open()) {
+                print $fh 'Update to '.$tablefile.' from Cluster Manager '.
+                          "$clientname ($clientip)\n";
+                $fh->close;
+            }
+        }
     }
 
-
     #  Indicate success:
  
     return "ok";
@@ -975,6 +1000,9 @@ sub read_profile {
 				 &GDBM_READER());
     if ($hashref) {
         my @queries=split(/\&/,$what);
+        if ($namespace eq 'roles') {
+            @queries = map { &unescape($_); } @queries; 
+        }
         my $qresult='';
 	
 	for (my $i=0;$i<=$#queries;$i++) {
@@ -1068,7 +1096,7 @@ sub pong_handler {
 #  Implicit Inputs:
 #      $currenthostid - Global variable that carries the name of the host
 #                       known as.
-#      $clientname    - Global variable that carries the name of the hsot we're connected to.
+#      $clientname    - Global variable that carries the name of the host we're connected to.
 #  Returns:
 #      1       - Ok to continue processing.
 #      0       - Program should exit.
@@ -1107,7 +1135,7 @@ sub establish_key_handler {
 #  Implicit Inputs:
 #      $currenthostid - Global variable that carries the name of the host
 #                       known as.
-#      $clientname    - Global variable that carries the name of the hsot we're connected to.
+#      $clientname    - Global variable that carries the name of the host we're connected to.
 #  Returns:
 #      1       - Ok to continue processing.
 #      0       - Program should exit.
@@ -1116,6 +1144,8 @@ sub establish_key_handler {
 sub load_handler {
     my ($cmd, $tail, $replyfd) = @_;
 
+
+
    # Get the load average from /proc/loadavg and calculate it as a percentage of
    # the allowed load limit as set by the perl global variable lonLoadLim
 
@@ -1144,7 +1174,7 @@ sub load_handler {
 #  Implicit Inputs:
 #      $currenthostid - Global variable that carries the name of the host
 #                       known as.
-#      $clientname    - Global variable that carries the name of the hsot we're connected to.
+#      $clientname    - Global variable that carries the name of the host we're connected to.
 #  Returns:
 #      1       - Ok to continue processing.
 #      0       - Program should exit
@@ -1613,6 +1643,74 @@ sub ls3_handler {
 }
 &register_handler("ls3", \&ls3_handler, 0, 1, 0);
 
+sub read_lonnet_global {
+    my ($cmd,$tail,$client) = @_;
+    my $userinput = "$cmd:$tail";
+    my $requested = &Apache::lonnet::thaw_unescape($tail);
+    my $result;
+    my %packagevars = (
+                        spareid => \%Apache::lonnet::spareid,
+                        perlvar => \%Apache::lonnet::perlvar,
+                      );
+    my %limit_to = (
+                    perlvar => {
+                                 lonOtherAuthen => 1,
+                                 lonBalancer    => 1,
+                                 lonVersion     => 1,
+                                 lonSysEMail    => 1,
+                                 lonHostID      => 1,
+                                 lonRole        => 1,
+                                 lonDefDomain   => 1,
+                                 lonLoadLim     => 1,
+                                 lonUserLoadLim => 1,
+                               }
+                  );
+    if (ref($requested) eq 'HASH') {
+        foreach my $what (keys(%{$requested})) {
+            my $response;
+            my $items = {};
+            if (exists($packagevars{$what})) {
+                if (ref($limit_to{$what}) eq 'HASH') {
+                    foreach my $varname (keys(%{$packagevars{$what}})) {
+                        if ($limit_to{$what}{$varname}) {
+                            $items->{$varname} = $packagevars{$what}{$varname};
+                        }
+                    }
+                } else {
+                    $items = $packagevars{$what};
+                }
+                if ($what eq 'perlvar') {
+                    if (!exists($packagevars{$what}{'lonBalancer'})) {
+                        if ($dist =~ /^(centos|rhes|fedora|scientific)/) {
+                            my $othervarref=LONCAPA::Configuration::read_conf('httpd.conf');
+                            if (ref($othervarref) eq 'HASH') {
+                                $items->{'lonBalancer'} = $othervarref->{'lonBalancer'};
+                            }
+                        }
+                    }
+                }
+                $response = &Apache::lonnet::freeze_escape($items);
+            }
+            $result .= &escape($what).'='.$response.'&';
+        }
+    }
+    $result =~ s/\&$//;
+    &Reply($client,\$result,$userinput);
+    return 1;
+}
+&register_handler("readlonnetglobal", \&read_lonnet_global, 0, 1, 0);
+
+sub server_devalidatecache_handler {
+    my ($cmd,$tail,$client) = @_;
+    my $userinput = "$cmd:$tail";
+    my ($name,$id) = map { &unescape($_); } split(/:/,$tail);
+    &Apache::lonnet::devalidate_cache_new($name,$id);
+    my $result = 'ok';
+    &Reply($client,\$result,$userinput);
+    return 1;
+}
+&register_handler("devalidatecache", \&server_devalidatecache_handler, 0, 1, 0);
+
 sub server_timezone_handler {
     my ($cmd,$tail,$client) = @_;
     my $userinput = "$cmd:$tail";
@@ -1653,6 +1751,23 @@ sub server_loncaparev_handler {
 }
 &register_handler("serverloncaparev", \&server_loncaparev_handler, 0, 1, 0);
 
+sub server_homeID_handler {
+    my ($cmd,$tail,$client) = @_;
+    my $userinput = "$cmd:$tail";
+    &Reply($client,\$perlvar{'lonHostID'},$userinput);
+    return 1;
+}
+&register_handler("serverhomeID", \&server_homeID_handler, 0, 1, 0);
+
+sub server_distarch_handler {
+    my ($cmd,$tail,$client) = @_;
+    my $userinput = "$cmd:$tail";
+    my $reply = &distro_and_arch();
+    &Reply($client,\$reply,$userinput);
+    return 1;
+}
+&register_handler("serverdistarch", \&server_distarch_handler, 0, 1, 0);
+
 #   Process a reinit request.  Reinit requests that either
 #   lonc or lond be reinitialized so that an updated 
 #   host.tab or domain.tab can be processed.
@@ -1762,15 +1877,49 @@ sub authenticate_handler {
     #  upass   - User's password.
     #  checkdefauth - Pass to validate_user() to try authentication
     #                 with default auth type(s) if no user account.
+    #  clientcancheckhost - Passed by clients with functionality in lonauth.pm
+    #                       to check if session can be hosted.
     
-    my ($udom, $uname, $upass, $checkdefauth)=split(/:/,$tail);
+    my ($udom, $uname, $upass, $checkdefauth, $clientcancheckhost)=split(/:/,$tail);
     &Debug(" Authenticate domain = $udom, user = $uname, password = $upass,  checkdefauth = $checkdefauth");
     chomp($upass);
     $upass=&unescape($upass);
 
     my $pwdcorrect = &validate_user($udom,$uname,$upass,$checkdefauth);
     if($pwdcorrect) {
-	&Reply( $client, "authorized\n", $userinput);
+        my $canhost = 1;
+        unless ($clientcancheckhost) {
+            my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
+            my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
+            my @intdoms;
+            my $internet_names = &Apache::lonnet::get_internet_names($clientname);
+            if (ref($internet_names) eq 'ARRAY') {
+                @intdoms = @{$internet_names};
+            }
+            unless ($uint_dom ne '' && grep(/^\Q$uint_dom\E$/,@intdoms)) {
+                my ($remote,$hosted);
+                my $remotesession = &get_usersession_config($udom,'remotesession');
+                if (ref($remotesession) eq 'HASH') {
+                    $remote = $remotesession->{'remote'}
+                }
+                my $hostedsession = &get_usersession_config($clienthomedom,'hostedsession');
+                if (ref($hostedsession) eq 'HASH') {
+                    $hosted = $hostedsession->{'hosted'};
+                }
+                my $loncaparev = $clientversion;
+                if ($loncaparev eq '') {
+                    $loncaparev = $Apache::lonnet::loncaparevs{$clientname};
+                }
+                $canhost = &Apache::lonnet::can_host_session($udom,$clientname,
+                                                             $loncaparev,
+                                                             $remote,$hosted);
+            }
+        }
+        if ($canhost) {               
+            &Reply( $client, "authorized\n", $userinput);
+        } else {
+            &Reply( $client, "not_allowed_to_host\n", $userinput);
+        }
 	#
 	#  Bad credentials: Failed to authorize
 	#
@@ -1924,7 +2073,7 @@ sub add_user_handler {
 		    ."makeuser";
 	    }
 	    unless ($fperror) {
-		my $result=&make_passwd_file($uname, $umode,$npass, $passfilename);
+		my $result=&make_passwd_file($uname,$udom,$umode,$npass, $passfilename);
 		&Reply($client,\$result, $userinput);     #BUGBUG - could be fail
 	    } else {
 		&Failure($client, \$fperror, $userinput);
@@ -1999,7 +2148,7 @@ sub change_authentication_handler {
 		    &Failure($client, \$result);
 		}
 	    } else {
-		my $result=&make_passwd_file($uname, $umode,$npass,$passfilename);
+		my $result=&make_passwd_file($uname,$udom,$umode,$npass,$passfilename);
 		#
 		#  If the current auth mode is internal, and the old auth mode was
 		#  unix, or krb*,  and the user is an author for this domain,
@@ -2061,16 +2210,10 @@ sub is_home_handler {
 &register_handler("home", \&is_home_handler, 0,1,0);
 
 #
-#   Process an update request for a resource?? I think what's going on here is
-#   that a resource has been modified that we hold a subscription to.
+#   Process an update request for a resource.
+#   A resource has been modified that we hold a subscription to.
 #   If the resource is not local, then we must update, or at least invalidate our
 #   cached copy of the resource. 
-#   FUTURE WORK:
-#      I need to look at this logic carefully.  My druthers would be to follow
-#      typical caching logic, and simple invalidate the cache, drop any subscription
-#      an let the next fetch start the ball rolling again... however that may
-#      actually be more difficult than it looks given the complex web of
-#      proxy servers.
 # Parameters:
 #    $cmd      - The command that got us here.
 #    $tail     - Tail of the command (remaining parameters).
@@ -2094,20 +2237,30 @@ sub update_resource_handler {
     my $ownership=ishome($fname);
     if ($ownership eq 'not_owner') {
 	if (-e $fname) {
+            # Delete preview file, if exists
+            unlink("$fname.tmp");
+            # Get usage stats
 	    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
 		$atime,$mtime,$ctime,$blksize,$blocks)=stat($fname);
 	    my $now=time;
 	    my $since=$now-$atime;
+            # If the file has not been used within lonExpire seconds,
+            # unsubscribe from it and delete local copy
 	    if ($since>$perlvar{'lonExpire'}) {
 		my $reply=&Apache::lonnet::reply("unsub:$fname","$clientname");
 		&devalidate_meta_cache($fname);
 		unlink("$fname");
 		unlink("$fname.meta");
 	    } else {
+            # Yes, this is in active use. Get a fresh copy. Since it might be in
+            # very active use and huge (like a movie), copy it to "in.transfer" filename first.
 		my $transname="$fname.in.transfer";
 		my $remoteurl=&Apache::lonnet::reply("sub:$fname","$clientname");
 		my $response;
-		alarm(120);
+# FIXME: cannot replicate files that take more than two minutes to transfer?
+#		alarm(120);
+# FIXME: this should use the LWP mechanism, not internal alarms.
+                alarm(1200);
 		{
 		    my $ua=new LWP::UserAgent;
 		    my $request=new HTTP::Request('GET',"$remoteurl");
@@ -2115,11 +2268,13 @@ sub update_resource_handler {
 		}
 		alarm(0);
 		if ($response->is_error()) {
+# FIXME: we should probably clean up here instead of just whine
 		    unlink($transname);
 		    my $message=$response->status_line;
 		    &logthis("LWP GET: $message for $fname ($remoteurl)");
 		} else {
 		    if ($remoteurl!~/\.meta$/) {
+# FIXME: isn't there an internal LWP mechanism for this?
 			alarm(120);
 			{
 			    my $ua=new LWP::UserAgent;
@@ -2131,6 +2286,7 @@ sub update_resource_handler {
 			}
 			alarm(0);
 		    }
+                    # we successfully transfered, copy file over to real name
 		    rename($transname,$fname);
 		    &devalidate_meta_cache($fname);
 		}
@@ -2192,7 +2348,10 @@ sub fetch_user_file_handler {
 
 	my $destname=$udir.'/'.$ufile;
 	my $transname=$udir.'/'.$ufile.'.in.transit';
-	my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;
+        my $clientprotocol=$Apache::lonnet::protocol{$clientname};
+        $clientprotocol = 'http' if ($clientprotocol ne 'https');
+	my $clienthost = &Apache::lonnet::hostname($clientname);
+	my $remoteurl=$clientprotocol.'://'.$clienthost.'/userfiles/'.$fname;
 	my $response;
 	Debug("Remote URL : $remoteurl Transfername $transname Destname: $destname");
 	alarm(120);
@@ -2368,7 +2527,6 @@ sub user_has_session_handler {
 
     my ($udom, $uname) = map { &unescape($_) } (split(/:/, $tail));
     
-    &logthis("Looking for $udom $uname");
     opendir(DIR,$perlvar{'lonIDsDir'});
     my $filename;
     while ($filename=readdir(DIR)) {
@@ -3099,7 +3257,7 @@ sub dump_with_regexp {
 
     my $userinput = "$cmd:$tail";
 
-    my ($udom,$uname,$namespace,$regexp,$range)=split(/:/,$tail);
+    my ($udom,$uname,$namespace,$regexp,$range,$extra)=split(/:/,$tail);
     if (defined($regexp)) {
 	$regexp=&unescape($regexp);
     } else {
@@ -3117,10 +3275,41 @@ sub dump_with_regexp {
     }
     my $hashref = &tie_user_hash($udom, $uname, $namespace,
 				 &GDBM_READER());
+    my $skipcheck;
     if ($hashref) {
         my $qresult='';
 	my $count=0;
+        if ($extra ne '') {
+            $extra = &Apache::lonnet::thaw_unescape($extra);
+            $skipcheck = $extra->{'skipcheck'};
+        }
+        my @ids = &Apache::lonnet::current_machine_ids();
+        my (%homecourses,$major,$minor,$now);
+        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;
+        }
 	while (my ($key,$value) = each(%$hashref)) {
+            if ($namespace eq 'roles') {
+                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,$end,$start) = split(/\_/,$value);
+                        if (!$end || $end > $now) {
+                            next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
+                                                            $minor,\%homecourses,\@ids));
+                        }
+                    }
+                }
+            }
 	    if ($regexp eq '.') {
 		$count++;
 		if (defined($range) && $count >= $end)   { last; }
@@ -3137,6 +3326,12 @@ sub dump_with_regexp {
 	    }
 	}
 	if (&untie_user_hash($hashref)) {
+            if (($namespace eq 'roles') && (!$skipcheck)) {
+                if (keys(%homecourses) > 0) {
+                    $qresult .= &check_homecourses(\%homecourses,$udom,$regexp,$count,
+                                                   $range,$start,$end,$major,$minor);
+                }
+            }
 	    chop($qresult);
 	    &Reply($client, \$qresult, $userinput);
 	} else {
@@ -3714,6 +3909,9 @@ sub put_course_id_hash_handler {
 #                 createdafter - include courses for which creation date followed this date.
 #                 creationcontext - include courses created in specified context 
 #
+#                 domcloner - flag to indicate if user can create CCs in course's domain.
+#                             If so, ability to clone course is automatic. 
+#
 #     $client  - The socket open on the client.
 # Returns:
 #    1     - Continue processing.
@@ -3726,7 +3924,7 @@ sub dump_course_id_handler {
     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) =split(/:/,$tail);
+        $creationcontext,$domcloner) =split(/:/,$tail);
     my $now = time;
     my ($cloneruname,$clonerudom,%cc_clone);
     if (defined($description)) {
@@ -3784,24 +3982,23 @@ sub dump_course_id_handler {
             $cc_clone{$clonedom.'_'.$clonenum} = 1;
         } 
     }
-    if (defined($createdbefore)) {
+    if ($createdbefore ne '') {
         $createdbefore = &unescape($createdbefore);
     } else {
        $createdbefore = 0;
     }
-    if (defined($createdafter)) {
+    if ($createdafter ne '') {
         $createdafter = &unescape($createdafter);
     } else {
         $createdafter = 0;
     }
-    if (defined($creationcontext)) {
+    if ($creationcontext ne '') {
         $creationcontext = &unescape($creationcontext);
     } else {
         $creationcontext = '.';
     }
-    
     my $unpack = 1;
-    if ($description eq '.' && $instcodefilter eq '.' && $coursefilter eq '.' && 
+    if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' && 
         $typefilter eq '.') {
         $unpack = 0;
     }
@@ -3827,10 +4024,12 @@ sub dump_course_id_handler {
             my $items = &Apache::lonnet::thaw_unescape($value);
             if (ref($items) eq 'HASH') {
                 if ($hashref->{$lasttime_key} eq '') {
-                    next if ($since > 0);
+                    next if ($since > 1);
                 }
                 $is_hash =  1;
-                if (defined($clonerudom)) {
+                if ($domcloner) {
+                    $canclone = 1;
+                } elsif (defined($clonerudom)) {
                     if ($items->{'cloners'}) {
                         my @cloneable = split(',',$items->{'cloners'});
                         if (@cloneable) {
@@ -3858,6 +4057,19 @@ sub dump_course_id_handler {
                             $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;
+                            }
+                        }
                     }
                 }
                 if ($unpack || !$rtn_as_hash) {
@@ -4058,6 +4270,53 @@ sub dump_course_id_handler {
 }
 &register_handler("courseiddump", \&dump_course_id_handler, 0, 1, 0);
 
+sub course_lastaccess_handler {
+    my ($cmd, $tail, $client) = @_;
+    my $userinput = "$cmd:$tail";
+    my ($cdom,$cnum) = split(':',$tail); 
+    my (%lastaccess,$qresult);
+    my $hashref = &tie_domain_hash($cdom, "nohist_courseids", &GDBM_WRCREAT());
+    if ($hashref) {
+        while (my ($key,$value) = each(%$hashref)) {
+            my ($unesc_key,$lasttime);
+            $unesc_key = &unescape($key);
+            if ($cnum) {
+                next unless ($unesc_key =~ /\Q$cdom\E_\Q$cnum\E$/);
+            }
+            if ($unesc_key =~ /^lasttime:($LONCAPA::match_domain\_$LONCAPA::match_courseid)/) {
+                $lastaccess{$1} = $value;
+            } else {
+                my $items = &Apache::lonnet::thaw_unescape($value);
+                if (ref($items) eq 'HASH') {
+                    unless ($lastaccess{$unesc_key}) {
+                        $lastaccess{$unesc_key} = '';
+                    }
+                } else {
+                    my @courseitems = split(':',$value);
+                    $lastaccess{$unesc_key} = pop(@courseitems);
+                }
+            }
+        }
+        foreach my $cid (sort(keys(%lastaccess))) {
+            $qresult.=&escape($cid).'='.$lastaccess{$cid}.'&'; 
+        }
+        if (&untie_domain_hash($hashref)) {
+            if ($qresult) {
+                chop($qresult);
+            }
+            &Reply($client, \$qresult, $userinput);
+        } else {
+            &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
+                    "while attempting lastacourseaccess\n", $userinput);
+        }
+    } else {
+        &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
+                "while attempting lastcourseaccess\n", $userinput);
+    }
+    return 1;
+}
+&register_handler("courselastaccess",\&course_lastaccess_handler, 0, 1, 0);
+
 #
 # Puts an unencrypted entry in a namespace db file at the domain level 
 #
@@ -4123,6 +4382,7 @@ sub put_domain_handler {
 sub get_domain_handler {
     my ($cmd, $tail, $client) = @_;
 
+
     my $userinput = "$client:$tail";
 
     my ($udom,$namespace,$what)=split(/:/,$tail,3);
@@ -4267,7 +4527,8 @@ sub get_id_handler {
 sub put_dcmail_handler {
     my ($cmd,$tail,$client) = @_;
     my $userinput = "$cmd:$tail";
-                                                                                
+
+
     my ($udom,$what)=split(/:/,$tail);
     chomp($what);
     my $hashref = &tie_domain_hash($udom, "nohist_dcmail", &GDBM_WRCREAT());
@@ -4531,7 +4792,7 @@ sub tmp_put_handler {
     }
     my ($id,$store);
     $tmpsnum++;
-    if ($context eq 'resetpw') {
+    if (($context eq 'resetpw') || ($context eq 'createaccount')) {
         $id = &md5_hex(&md5_hex(time.{}.rand().$$));
     } else {
         $id = $$.'_'.$clientip.'_'.$tmpsnum;
@@ -4849,10 +5110,11 @@ sub get_sections_handler {
 sub validate_course_owner_handler {
     my ($cmd, $tail, $client)  = @_;
     my $userinput = "$cmd:$tail";
-    my ($inst_course_id, $owner, $cdom) = split(/:/, $tail);
-
+    my ($inst_course_id, $owner, $cdom, $coowners) = split(/:/, $tail);
+    
     $owner = &unescape($owner);
-    my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom);
+    $coowners = &unescape($coowners);
+    my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom,$coowners);
     &Reply($client, \$outcome, $userinput);
 
 
@@ -5840,7 +6102,7 @@ if (-e $pidfile) {
 $server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'},
                                 Type      => SOCK_STREAM,
                                 Proto     => 'tcp',
-                                Reuse     => 1,
+                                ReuseAddr     => 1,
                                 Listen    => 10 )
   or die "making socket: $@\n";
 
@@ -5903,9 +6165,13 @@ sub HUPSMAN {                      # sig
 #  a setuid perl script that can be root for us to do this job.
 #
 sub ReloadApache {
-    my $execdir = $perlvar{'lonDaemons'};
-    my $script  = $execdir."/apachereload";
-    system($script);
+# --------------------------- Handle case of another apachereload process (locking)
+    if (&LONCAPA::try_to_lock('/tmp/lock_apachereload')) {
+        my $execdir = $perlvar{'lonDaemons'};
+        my $script  = $execdir."/apachereload";
+        system($script);
+        unlink('/tmp/lock_apachereload'); #  Remove the lock file.
+    }
 }
 
 #
@@ -6078,7 +6344,7 @@ sub logstatus {
 sub initnewstatus {
     my $docdir=$perlvar{'lonDocRoot'};
     my $fh=IO::File->new(">$docdir/lon-status/londstatus.txt");
-    my $now=time;
+    my $now=time();
     my $local=localtime($now);
     print $fh "LOND status $local - parent $$\n\n";
     opendir(DIR,"$docdir/lon-status/londchld");
@@ -6167,8 +6433,16 @@ $SIG{USR2} = \&UpdateHosts;
 
 #  Read the host hashes:
 &Apache::lonnet::load_hosts_tab();
+my %iphost = &Apache::lonnet::get_iphost(1);
+
+$dist=`$perlvar{'lonDaemons'}/distprobe`;
 
-my $dist=`$perlvar{'lonDaemons'}/distprobe`;
+my $arch = `uname -i`;
+chomp($arch);
+if ($arch eq 'unknown') {
+    $arch = `uname -m`;
+    chomp($arch);
+}
 
 # --------------------------------------------------------------
 #   Accept connections.  When a connection comes in, it is validated
@@ -6227,6 +6501,7 @@ sub make_new_child {
             or die "Can't unblock SIGINT for fork: $!\n";
         $children{$pid} = $clientip;
         &status('Started child '.$pid);
+	close($client);
         return;
     } else {
         # Child can *not* return from this subroutine.
@@ -6235,6 +6510,13 @@ sub make_new_child {
                                 #don't get intercepted
         $SIG{USR1}= \&logstatus;
         $SIG{ALRM}= \&timeout;
+	#
+	# Block sigpipe as it gets thrownon socket disconnect and we want to 
+	# deal with that as a read faiure instead.
+	#
+	my $blockset = POSIX::SigSet->new(SIGPIPE);
+	sigprocmask(SIG_BLOCK, $blockset);
+
         $lastlog='Forked ';
         $status='Forked';
 
@@ -6263,7 +6545,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];
@@ -6291,7 +6573,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 
@@ -6311,6 +6593,7 @@ sub make_new_child {
 		}
 
 		if($inittype eq "local") {
+                    $clientversion = $perlvar{'lonVersion'};
 		    my $key = LocalConnection($client, $remotereq);
 		    if($key) {
 			Debug("Got local key $key");
@@ -6318,7 +6601,7 @@ sub make_new_child {
 			my $cipherkey = pack("H32", $key);
 			$cipher       = new IDEA($cipherkey);
 			print $client "ok:local\n";
-			&logthis('<font color="green"'
+			&logthis('<font color="green">'
 				 . "Successful local authentication </font>");
 			$keymode = "local"
 		    } else {
@@ -6382,6 +6665,9 @@ sub make_new_child {
 # ------------------------------------------------------------ Process requests
 	    my $keep_going = 1;
 	    my $user_input;
+            my $clienthost = &Apache::lonnet::hostname($clientname);
+            my $clientserverhomeID = &Apache::lonnet::get_server_homeID($clienthost);
+            $clienthomedom = &Apache::lonnet::host_domain($clientserverhomeID);
 	    while(($user_input = get_request) && $keep_going) {
 		alarm(120);
 		Debug("Main: Got $user_input\n");
@@ -6445,8 +6731,7 @@ sub is_author {
 }
 #
 #   Checks to see if the input roleput request was to set
-# an author role.  If so, invokes the lchtmldir script to set
-# up a correct public_html 
+# an author role.  If so, creates construction space 
 # Parameters:
 #    request   - The request sent to the rolesput subchunk.
 #                We're looking for  /domain/_au
@@ -6456,16 +6741,15 @@ sub is_author {
 #
 sub manage_permissions {
     my ($request, $domain, $user, $authtype) = @_;
-
-    &Debug("manage_permissions: $request $domain $user $authtype");
-
     # See if the request is of the form /$domain/_au
     if($request =~ /^(\/\Q$domain\E\/_au)$/) { # It's an author rolesput...
-	my $execdir = $perlvar{'lonDaemons'};
-	my $userhome= "/home/$user" ;
-	&logthis("system $execdir/lchtmldir $userhome $user $authtype");
-	&Debug("Setting homedir permissions for $userhome");
-	system("$execdir/lchtmldir $userhome $user $authtype");
+        my $path=$perlvar{'lonDocRoot'}."/priv/$domain";
+        unless (-e $path) {        
+           mkdir($path);
+        }
+        unless (-e $path.'/'.$user) {
+           mkdir($path.'/'.$user);
+        }
     }
 }
 
@@ -6540,9 +6824,7 @@ sub rewrite_password_file {
 
 #     Returns the authorization type or nouser if there is no such user.
 #
-sub get_auth_type 
-{
-
+sub get_auth_type {
     my ($domain, $user)  = @_;
 
     Debug("get_auth_type( $domain, $user ) \n");
@@ -6637,54 +6919,24 @@ sub validate_user {
 	    } else {
 		$validated = 0;
 	    }
-	}
-	elsif ($howpwd eq "krb4") { # user is in kerberos 4 auth. domain.
-	    if(! ($password =~ /$null/) ) {
-		my $k4error = &Authen::Krb4::get_pw_in_tkt($user,
-							   "",
-							   $contentpwd,,
-							   'krbtgt',
-							   $contentpwd,
-							   1,
-							   $password);
-		if(!$k4error) {
-		    $validated = 1;
-		} else {
-		    $validated = 0;
-		    &logthis('krb4: '.$user.', '.$contentpwd.', '.
-			     &Authen::Krb4::get_err_txt($Authen::Krb4::error));
-		}
-	    } else {
-		$validated = 0; # Password has a match with null.
-	    }
+	} elsif ($howpwd eq "krb4") { # user is in kerberos 4 auth. domain.
+            my $checkwithkrb5 = 0;
+            if ($dist =~/^fedora(\d+)$/) {
+                if ($1 > 11) {
+                    $checkwithkrb5 = 1;
+                }
+            } elsif ($dist =~ /^suse([\d.]+)$/) {
+                if ($1 > 11.1) {
+                    $checkwithkrb5 = 1; 
+                }
+            }
+            if ($checkwithkrb5) {
+                $validated = &krb5_authen($password,$null,$user,$contentpwd);
+            } else {
+                $validated = &krb4_authen($password,$null,$user,$contentpwd);
+            }
 	} elsif ($howpwd eq "krb5") { # User is in kerberos 5 auth. domain.
-	    if(!($password =~ /$null/)) { # Null password not allowed.
-		my $krbclient = &Authen::Krb5::parse_name($user.'@'
-							  .$contentpwd);
-		my $krbservice = "krbtgt/".$contentpwd."\@".$contentpwd;
-		my $krbserver  = &Authen::Krb5::parse_name($krbservice);
-		my $credentials= &Authen::Krb5::cc_default();
-		$credentials->initialize(&Authen::Krb5::parse_name($user.'@'
-                                                                 .$contentpwd));
-                my $krbreturn;
-                if (exists(&Authen::Krb5::get_init_creds_password)) {
-                    $krbreturn = 
-                        &Authen::Krb5::get_init_creds_password($krbclient,$password,
-                                                               $krbservice);
-                    $validated = (ref($krbreturn) eq 'Authen::Krb5::Creds');
-                } else {
-		    $krbreturn  = 
-                        &Authen::Krb5::get_in_tkt_with_password($krbclient,$krbserver,
-		 						$password,$credentials);
-		    $validated = ($krbreturn == 1);
-                }
-		if (!$validated) {
-		    &logthis('krb5: '.$user.', '.$contentpwd.', '.
-			     &Authen::Krb5::error());
-		}
-	    } else {
-		$validated = 0;
-	    }
+            $validated = &krb5_authen($password,$null,$user,$contentpwd);
 	} elsif ($howpwd eq "localauth") { 
 	    #  Authenticate via installation specific authentcation method:
 	    $validated = &localauth::localauth($user, 
@@ -6715,6 +6967,65 @@ sub validate_user {
     return $validated;
 }
 
+sub krb4_authen {
+    my ($password,$null,$user,$contentpwd) = @_;
+    my $validated = 0;
+    if (!($password =~ /$null/) ) {  # Null password not allowed.
+        eval {
+            require Authen::Krb4;
+        };
+        if (!$@) {
+            my $k4error = &Authen::Krb4::get_pw_in_tkt($user,
+                                                       "",
+                                                       $contentpwd,,
+                                                       'krbtgt',
+                                                       $contentpwd,
+                                                       1,
+                                                       $password);
+            if(!$k4error) {
+                $validated = 1;
+            } else {
+                $validated = 0;
+                &logthis('krb4: '.$user.', '.$contentpwd.', '.
+                          &Authen::Krb4::get_err_txt($Authen::Krb4::error));
+            }
+        } else {
+            $validated = krb5_authen($password,$null,$user,$contentpwd);
+        }
+    }
+    return $validated;
+}
+
+sub krb5_authen {
+    my ($password,$null,$user,$contentpwd) = @_;
+    my $validated = 0;
+    if(!($password =~ /$null/)) { # Null password not allowed.
+        my $krbclient = &Authen::Krb5::parse_name($user.'@'
+                                                  .$contentpwd);
+        my $krbservice = "krbtgt/".$contentpwd."\@".$contentpwd;
+        my $krbserver  = &Authen::Krb5::parse_name($krbservice);
+        my $credentials= &Authen::Krb5::cc_default();
+        $credentials->initialize(&Authen::Krb5::parse_name($user.'@'
+                                                            .$contentpwd));
+        my $krbreturn;
+        if (exists(&Authen::Krb5::get_init_creds_password)) {
+            $krbreturn =
+                &Authen::Krb5::get_init_creds_password($krbclient,$password,
+                                                          $krbservice);
+            $validated = (ref($krbreturn) eq 'Authen::Krb5::Creds');
+        } else {
+            $krbreturn  =
+                &Authen::Krb5::get_in_tkt_with_password($krbclient,$krbserver,
+                                                         $password,$credentials);
+            $validated = ($krbreturn == 1);
+        }
+        if (!$validated) {
+            &logthis('krb5: '.$user.', '.$contentpwd.', '.
+                     &Authen::Krb5::error());
+        }
+    }
+    return $validated;
+}
 
 sub addline {
     my ($fname,$hostid,$ip,$newline)=@_;
@@ -6927,7 +7238,9 @@ sub subscribe {
                 # the metadata
 		unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); }
 		$fname=~s/\/home\/httpd\/html\/res/raw/;
-		$fname="http://".&Apache::lonnet::hostname($perlvar{'lonHostID'})."/".$fname;
+                my $protocol = $Apache::lonnet::protocol{$perlvar{'lonHostID'}};
+                $protocol = 'http' if ($protocol ne 'https');
+		$fname=$protocol.'://'.&Apache::lonnet::hostname($perlvar{'lonHostID'})."/".$fname;
 		$result="$fname\n";
 	    }
 	} else {
@@ -6969,7 +7282,7 @@ sub change_unix_password {
 
 
 sub make_passwd_file {
-    my ($uname, $umode,$npass,$passfilename)=@_;
+    my ($uname,$udom,$umode,$npass,$passfilename)=@_;
     my $result="ok";
     if ($umode eq 'krb4' or $umode eq 'krb5') {
 	{
@@ -7010,7 +7323,7 @@ sub make_passwd_file {
 	    #
 	    my $uid = getpwnam($uname);
 	    if((defined $uid) && ($uid == 0)) {
-		&logthis(">>>Attempted to create privilged account blocked");
+		&logthis(">>>Attempt to create privileged account blocked");
 		return "no_priv_account_error\n";
 	    }
 
@@ -7022,6 +7335,7 @@ sub make_passwd_file {
 		&Debug("user  = ".$uname.", Password =". $npass);
 		my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log");
 		print $se "$uname\n";
+                print $se "$udom\n";
 		print $se "$npass\n";
 		print $se "$npass\n";
 		print $se "$lc_error_file\n"; # Status -> unique file.
@@ -7085,7 +7399,7 @@ sub sethost {
 	eq &Apache::lonnet::get_host_ip($hostid)) {
 	$currenthostid  =$hostid;
 	$currentdomainid=&Apache::lonnet::host_domain($hostid);
-	&logthis("Setting hostid to $hostid, and domain to $currentdomainid");
+#	&logthis("Setting hostid to $hostid, and domain to $currentdomainid");
     } else {
 	&logthis("Requested host id $hostid not an alias of ".
 		 $perlvar{'lonHostID'}." refusing connection");
@@ -7100,6 +7414,186 @@ sub version {
     return "version:$VERSION";
 }
 
+sub get_usersession_config {
+    my ($dom,$name) = @_;
+    my ($usersessionconf,$cached)=&Apache::lonnet::is_cached_new($name,$dom);
+    if (defined($cached)) {
+        return $usersessionconf;
+    } else {
+        my %domconfig = &Apache::lonnet::get_dom('configuration',['usersessions'],$dom);
+        if (ref($domconfig{'usersessions'}) eq 'HASH') {
+            &Apache::lonnet::do_cache_new($name,$dom,$domconfig{'usersessions'},3600);
+            return $domconfig{'usersessions'};
+        }
+    }
+    return;
+}
+
+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));
+            }
+        }
+    } else {
+        if (ref($ids) eq 'ARRAY') {
+            if (grep(/^\Q$home\E$/,@{$ids})) {
+                if (ref($homecourses) eq 'HASH') {
+                    if (ref($homecourses->{$hashid}) eq 'ARRAY') {
+                        push(@{$homecourses->{$hashid}},{$key=>$value});
+                    } else {
+                        $homecourses->{$hashid} = [{$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 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 check_homecourses {
+    my ($homecourses,$udom,$regexp,$count,$range,$start,$end,$major,$minor) = @_;
+    my ($result,%addtocache);
+    my $yesterday = time - 24*3600; 
+    if (ref($homecourses) eq 'HASH') {
+        my (%okcourses,%courseinfo,%recent);
+        my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT());
+        if ($hashref) {
+            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 $hashid = $unesc_key;
+                    $hashid =~ s/_/:/;
+                    $courseinfo{$hashid} = $items;
+                    if (ref($homecourses->{$hashid}) 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');
+            }
+        } else {
+            &logthis('Failed to tie hash for nohist_courseids.db');
+            return;
+        }
+        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 $hashid (keys(%{$homecourses})) {
+            next if ($recent{$hashid});
+            &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
+        }
+        foreach my $hashid (keys(%okcourses)) {
+            if (ref($homecourses->{$hashid}) eq 'ARRAY') {
+                foreach my $role (@{$homecourses->{$hashid}}) {
+                    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;
+}
+
+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 distro_and_arch {
+    return $dist.':'.$arch;
+}
 
 # ----------------------------------- POD (plain old documentation, CPAN style)
 
@@ -7835,5 +8329,7 @@ string.
 
 =back
 
+=back
+
 
 =cut