--- loncom/lond	2007/04/04 00:02:50	1.369
+++ loncom/lond	2007/10/13 17:07:56	1.388
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
 #
-# $Id: lond,v 1.369 2007/04/04 00:02:50 albertel Exp $
+# $Id: lond,v 1.388 2007/10/13 17:07:56 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -33,7 +33,6 @@ use strict;
 use lib '/home/httpd/lib/perl/';
 use LONCAPA;
 use LONCAPA::Configuration;
-use Apache::lonnet;
 
 use IO::Socket;
 use IO::File;
@@ -53,13 +52,14 @@ use File::Find;
 use LONCAPA::lonlocal;
 use LONCAPA::lonssl;
 use Fcntl qw(:flock);
+use Apache::lonnet;
 
 my $DEBUG = 0;		       # Non zero to enable debug log entries.
 
 my $status='';
 my $lastlog='';
 
-my $VERSION='$Revision: 1.369 $'; #' stupid emacs
+my $VERSION='$Revision: 1.388 $'; #' stupid emacs
 my $remoteVERSION;
 my $currenthostid="default";
 my $currentdomainid;
@@ -69,7 +69,6 @@ my $clientip;			# IP address of client.
 my $clientname;			# LonCAPA name of client.
 
 my $server;
-my $thisserver;			# DNS of us.
 
 my $keymode;
 
@@ -136,7 +135,7 @@ my @adderrors    = ("ok",
 		    "lcuseradd Unable to make www member of users's group",
 		    "lcuseradd Unable to su to root",
 		    "lcuseradd Unable to set password",
-		    "lcuseradd Usrname has invalid characters",
+		    "lcuseradd Username has invalid characters",
 		    "lcuseradd Password has an invalid character",
 		    "lcuseradd User already exists",
 		    "lcuseradd Could not add user.",
@@ -172,19 +171,16 @@ sub ResetStatistics {
 #   $Socket      - Socket open on client.
 #   $initcmd     - The full text of the init command.
 #
-# Implicit inputs:
-#    $thisserver - Our DNS name.
-#
 # Returns:
 #     IDEA session key on success.
 #     undef on failure.
 #
 sub LocalConnection {
     my ($Socket, $initcmd) = @_;
-    Debug("Attempting local connection: $initcmd client: $clientip me: $thisserver");
+    Debug("Attempting local connection: $initcmd client: $clientip");
     if($clientip ne "127.0.0.1") {
 	&logthis('<font color="red"> LocalConnection rejecting non local: '
-		 ."$clientip ne $thisserver </font>");
+		 ."$clientip ne 127.0.0.1 </font>");
 	close $Socket;
 	return undef;
     }  else {
@@ -1000,7 +996,7 @@ sub ping_handler {
     my ($cmd, $tail, $client) = @_;
     Debug("$cmd $tail $client .. $currenthostid:");
    
-    Reply( $client,"$currenthostid\n","$cmd:$tail");
+    Reply( $client,\$currenthostid,"$cmd:$tail");
    
     return 1;
 }
@@ -1070,7 +1066,7 @@ sub establish_key_handler {
     $key=substr($key,0,32);
     my $cipherkey=pack("H32",$key);
     $cipher=new IDEA $cipherkey;
-    &Reply($replyfd, "$buildkey\n", "$cmd:$tail"); 
+    &Reply($replyfd, \$buildkey, "$cmd:$tail"); 
    
     return 1;
 
@@ -1107,7 +1103,7 @@ sub load_handler {
    
     my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
 
-    &Reply( $replyfd, "$loadpercent\n", "$cmd:$tail");
+    &Reply( $replyfd, \$loadpercent, "$cmd:$tail");
    
     return 1;
 }
@@ -1137,7 +1133,7 @@ sub user_load_handler {
     my ($cmd, $tail, $replyfd) = @_;
 
     my $userloadpercent=&Apache::lonnet::userload();
-    &Reply($replyfd, "$userloadpercent\n", "$cmd:$tail");
+    &Reply($replyfd, \$userloadpercent, "$cmd:$tail");
     
     return 1;
 }
@@ -1180,7 +1176,7 @@ sub user_authorization_type {
 	} else {
             $type .= ':';
         }
-	&Reply( $replyfd, "$type\n", $userinput);
+	&Reply( $replyfd, \$type, $userinput);
     }
   
     return 1;
@@ -1216,7 +1212,7 @@ sub push_file_handler {
 	# process making the request.
       
 	my $reply = &PushFile($userinput);
-	&Reply($client, "$reply\n", $userinput);
+	&Reply($client, \$reply, $userinput);
 
     } else {
 	&Failure( $client, "refused\n", $userinput);
@@ -1268,7 +1264,7 @@ sub du_handler {
 	chdir($ududir);
 	find($code,$ududir);
 	$total_size=int($total_size/1024);
-	&Reply($client,"$total_size\n","$cmd:$ududir");
+	&Reply($client,\$total_size,"$cmd:$ududir");
     } else {
 	&Failure($client, "bad_directory:$ududir\n","$cmd:$ududir"); 
     }
@@ -1337,7 +1333,7 @@ sub ls_handler {
 	$ulsout='no_such_dir';
     }
     if ($ulsout eq '') { $ulsout='empty'; }
-    &Reply($client, "$ulsout\n", $userinput); # This supports debug logging.
+    &Reply($client, \$ulsout, $userinput); # This supports debug logging.
     
     return 1;
 
@@ -1406,7 +1402,7 @@ sub ls2_handler {
         $ulsout='no_such_dir';
    }
    if ($ulsout eq '') { $ulsout='empty'; }
-   &Reply($client, "$ulsout\n", $userinput); # This supports debug logging.
+   &Reply($client, \$ulsout, $userinput); # This supports debug logging.
    return 1;
 }
 &register_handler("ls2", \&ls2_handler, 0, 1, 0);
@@ -1434,7 +1430,7 @@ sub reinit_process_handler {
     if(&ValidManager($cert)) {
 	chomp($userinput);
 	my $reply = &ReinitProcess($userinput);
-	&Reply( $client,  "$reply\n", $userinput);
+	&Reply( $client,  \$reply, $userinput);
     } else {
 	&Failure( $client, "refused\n", $userinput);
     }
@@ -1609,7 +1605,7 @@ sub change_password_handler {
 	    my $result = &change_unix_password($uname, $npass);
 	    &logthis("Result of password change for $uname: ".
 		     $result);
-	    &Reply($client, "$result\n", $userinput);
+	    &Reply($client, \$result, $userinput);
 	} else {
 	    # this just means that the current password mode is not
 	    # one we know how to change (e.g the kerberos auth modes or
@@ -1672,7 +1668,7 @@ sub add_user_handler {
 		my $result=&make_passwd_file($uname, $umode,$npass, $passfilename);
 		&Reply($client, $result, $userinput);     #BUGBUG - could be fail
 	    } else {
-		&Failure($client, "$fperror\n", $userinput);
+		&Failure($client, \$fperror, $userinput);
 	    }
 	}
 	umask($oldumask);
@@ -1739,9 +1735,9 @@ sub change_authentication_handler {
 		my $result = &change_unix_password($uname, $npass);
 		&logthis("Result of password change for $uname: ".$result);
 		if ($result eq "ok") {
-		    &Reply($client, "$result\n")
+		    &Reply($client, \$result)
 		} else {
-		    &Failure($client, "$result\n");
+		    &Failure($client, \$result);
 		}
 	    } else {
 		my $result=&make_passwd_file($uname, $umode,$npass,$passfilename);
@@ -1760,7 +1756,7 @@ sub change_authentication_handler {
 			&manage_permissions("/$udom/_au", $udom, $uname, "$umode:");
 		    }
 		}
-		&Reply($client, $result, $userinput);
+		&Reply($client, \$result, $userinput);
 	    }
 	       
 
@@ -2099,6 +2095,37 @@ sub rename_user_file_handler {
 &register_handler("renameuserfile", \&rename_user_file_handler, 0,1,0);
 
 #
+#  Checks if the specified user has an active session on the server
+#  return ok if so, not_found if not
+#
+# Parameters:
+#   cmd      - The request keyword that dispatched to tus.
+#   tail     - The tail of the request (colon separated parameters).
+#   client   - Filehandle open on the client.
+# Return:
+#    1.
+sub user_has_session_handler {
+    my ($cmd, $tail, $client) = @_;
+
+    my ($udom, $uname) = map { &unescape($_) } (split(/:/, $tail));
+    
+    &logthis("Looking for $udom $uname");
+    opendir(DIR,$perlvar{'lonIDsDir'});
+    my $filename;
+    while ($filename=readdir(DIR)) {
+	last if ($filename=~/^\Q$uname\E_\d+_\Q$udom\E_/);
+    }
+    if ($filename) {
+	&Reply($client, "ok\n", "$cmd:$tail");
+    } else {
+	&Failure($client, "not_found\n", "$cmd:$tail");
+    }
+    return 1;
+
+}
+&register_handler("userhassession", \&user_has_session_handler, 0,1,0);
+
+#
 #  Authenticate access to a user file by checking that the token the user's 
 #  passed also exists in their session file
 #
@@ -2131,7 +2158,7 @@ sub token_auth_user_file_handler {
 	}
 	untie(%disk_env);
 	close(ENVIN);
-	&Reply($client, $reply, "$cmd:$tail");
+	&Reply($client, \$reply, "$cmd:$tail");
     } else {
 	&Failure($client, "invalid_token\n", "$cmd:$tail");
     }
@@ -2191,13 +2218,13 @@ sub subscribe_handler {
 &register_handler("sub", \&subscribe_handler, 0, 1, 0);
 
 #
-#   Determine the version of a resource (?) Or is it return
-#   the top version of the resource?  Not yet clear from the
-#   code in currentversion.
+#   Determine the latest version of a resource (it looks for the highest
+#   past version and then returns that +1)
 #
 # Parameters:
 #    $cmd      - The command that got us here.
 #    $tail     - Tail of the command (remaining parameters).
+#                 (Should consist of an absolute path to a file)
 #    $client   - File descriptor connected to client.
 # Returns
 #     0        - Requested to exit, caller should shut down.
@@ -2558,7 +2585,7 @@ sub get_profile_entry {
     my $replystring = read_profile($udom, $uname, $namespace, $what);
     my ($first) = split(/:/,$replystring);
     if($first ne "error") {
-	&Reply($client, "$replystring\n", $userinput);
+	&Reply($client, \$replystring, $userinput);
     } else {
 	&Failure($client, $replystring." while attempting get\n", $userinput);
     }
@@ -2698,7 +2725,7 @@ sub get_profile_keys {
 	}
 	if (&untie_user_hash($hashref)) {
 	    $qresult=~s/\&$//;
-	    &Reply($client, "$qresult\n", $userinput);
+	    &Reply($client, \$qresult, $userinput);
 	} else {
 	    &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
 		    "while attempting keys\n", $userinput);
@@ -2768,7 +2795,7 @@ sub dump_profile_database {
 		}
 	    }
 	    chop($qresult);
-	    &Reply($client , "$qresult\n", $userinput);
+	    &Reply($client , \$qresult, $userinput);
 	} else {
 	    &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
 		     "while attempting currentdump\n", $userinput);
@@ -2851,7 +2878,7 @@ sub dump_with_regexp {
 	}
 	if (&untie_user_hash($hashref)) {
 	    chop($qresult);
-	    &Reply($client, "$qresult\n", $userinput);
+	    &Reply($client, \$qresult, $userinput);
 	} else {
 	    &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
 		     "while attempting dump\n", $userinput);
@@ -3059,7 +3086,7 @@ sub restore_handler {
 	}
 	if (&untie_user_hash($hashref)) {
 	    $qresult=~s/\&$//;
-	    &Reply( $client, "$qresult\n", $userinput);
+	    &Reply( $client, \$qresult, $userinput);
 	} else {
 	    &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
 		    "while attempting restore\n", $userinput);
@@ -3140,7 +3167,7 @@ sub retrieve_chat_handler {
 	$reply.=&escape($_).':';
     }
     $reply=~s/\:$//;
-    &Reply($client, $reply."\n", $userinput);
+    &Reply($client, \$reply, $userinput);
 
 
     return 1;
@@ -3277,6 +3304,22 @@ sub put_course_id_handler {
 	foreach my $pair (@pairs) {
             my ($key,$courseinfo) = split(/=/,$pair,2);
             $courseinfo =~ s/=/:/g;
+            if (defined($hashref->{$key})) {
+                my $value = &Apache::lonnet::thaw_unescape($hashref->{$key});
+                if (ref($value) eq 'HASH') {
+                    my @items = ('description','inst_code','owner','type');
+                    my @new_items = split(/:/,$courseinfo,-1);
+                    my %storehash; 
+                    for (my $i=0; $i<@new_items; $i++) {
+                        $storehash{$items[$i]} = $new_items[$i];
+                    }
+                    $hashref->{$key} = 
+                        &Apache::lonnet::freeze_escape(\%storehash);
+                    my $unesc_key = &unescape($key);
+                    $hashref->{&escape('lasttime:'.$unesc_key)} = $now;
+                    next;
+                }
+            }
             my @current_items = split(/:/,$hashref->{$key},-1);
             shift(@current_items); # remove description
             pop(@current_items);   # remove last access
@@ -3293,7 +3336,7 @@ sub put_course_id_handler {
                     }
                 }
             }
-	    $hashref->{$key}=$courseinfo.':'.$now;
+            $hashref->{$key}=$courseinfo.':'.$now;
 	}
 	if (&untie_domain_hash($hashref)) {
 	    &Reply( $client, "ok\n", $userinput);
@@ -3307,12 +3350,54 @@ sub put_course_id_handler {
 		 ." tie(GDBM) Failed ".
 		 "while attempting courseidput\n", $userinput);
     }
-    
 
     return 1;
 }
 &register_handler("courseidput", \&put_course_id_handler, 0, 1, 0);
 
+sub put_course_id_hash_handler {
+    my ($cmd, $tail, $client) = @_;
+    my $userinput = "$cmd:$tail";
+    my ($udom,$mode,$what) = split(/:/, $tail,3);
+    chomp($what);
+    my $now=time;
+    my @pairs=split(/\&/,$what);
+    my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT());
+    if ($hashref) {
+        foreach my $pair (@pairs) {
+            my ($key,$value)=split(/=/,$pair);
+            my $unesc_key = &unescape($key);
+            if ($mode ne 'timeonly') {
+                if (!defined($hashref->{&escape('lasttime:'.$unesc_key)})) {
+                    my $curritems = &Apache::lonnet::thaw_unescape($key); 
+                    if (ref($curritems) ne 'HASH') {
+                        my @current_items = split(/:/,$hashref->{$key},-1);
+                        my $lasttime = pop(@current_items);
+                        $hashref->{&escape('lasttime:'.$unesc_key)} = $lasttime;
+                    } else {
+                        $hashref->{&escape('lasttime:'.$unesc_key)} = '';
+                    }
+                } 
+                $hashref->{$key} = $value;
+            }
+            if ($mode ne 'notime') {
+                $hashref->{&escape('lasttime:'.$unesc_key)} = $now;
+            }
+        }
+        if (&untie_domain_hash($hashref)) {
+            &Reply($client, "ok\n", $userinput);
+        } else {
+            &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
+                     "while attempting courseidputhash\n", $userinput);
+        }
+    } else {
+        &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ".
+                  "while attempting courseidputhash\n", $userinput);
+    }
+    return 1;
+}
+&register_handler("courseidputhash", \&put_course_id_hash_handler, 0, 1, 0);
+
 #  Retrieves the value of a course id resource keyword pattern
 #  defined since a starting date.  Both the starting date and the
 #  keyword pattern are optional.  If the starting date is not supplied it
@@ -3339,6 +3424,15 @@ sub put_course_id_handler {
 #                            owner matches the supplied username and/or domain
 #                            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.  
+#                 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 
+#                            colon-separated list of (in order) description,
+#                            institutional code, and course owner.
+#    
 #     $client  - The socket open on the client.
 # Returns:
 #    1     - Continue processing.
@@ -3346,11 +3440,10 @@ sub put_course_id_handler {
 #   a reply is written to $client.
 sub dump_course_id_handler {
     my ($cmd, $tail, $client) = @_;
-
     my $userinput = "$cmd:$tail";
 
     my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter,
-        $typefilter,$regexp_ok) =split(/:/,$tail);
+        $typefilter,$regexp_ok,$rtn_as_hash) =split(/:/,$tail);
     if (defined($description)) {
 	$description=&unescape($description);
     } else {
@@ -3390,62 +3483,94 @@ sub dump_course_id_handler {
     if (defined($regexp_ok)) {
         $regexp_ok=&unescape($regexp_ok);
     }
-
-    unless (defined($since)) { $since=0; }
+    my $unpack = 1;
+    if ($description eq '.' && $instcodefilter eq '.' && $coursefilter eq '.' && 
+        $typefilter eq '.') {
+        $unpack = 0;
+    }
+    if (!defined($since)) { $since=0; }
     my $qresult='';
     my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT());
     if ($hashref) {
 	while (my ($key,$value) = each(%$hashref)) {
-	    my ($descr,$lasttime,$inst_code,$owner,$type);
-            my @courseitems = split(/:/,$value);
-            $lasttime = pop(@courseitems);
-	    ($descr,$inst_code,$owner,$type)=@courseitems;
-	    if ($lasttime<$since) { next; }
+            my ($unesc_key,$lasttime_key,$lasttime,$is_hash,%val,%unesc_val);
+            $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 $items = &Apache::lonnet::thaw_unescape($value);
+            if (ref($items) eq 'HASH') {
+                $is_hash =  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'};
+                }
+            } else {
+                $is_hash =  0;
+                my @courseitems = split(/:/,$value);
+                $lasttime = pop(@courseitems);
+                next if ($lasttime<$since);
+	        ($val{'descr'},$val{'inst_code'},$val{'owner'},$val{'type'}) = @courseitems;
+            }
             my $match = 1;
-	    unless ($description eq '.') {
-		my $unescapeDescr = &unescape($descr);
-		unless (eval('$unescapeDescr=~/\Q$description\E/i')) {
+	    if ($description ne '.') {
+                if (!$is_hash) {
+                    $unesc_val{'descr'} = &unescape($val{'descr'});
+                }
+                if (eval{$unesc_val{'descr'} !~ /\Q$description\E/i}) {
                     $match = 0;
-		}
+                }
             }
-            unless ($instcodefilter eq '.' || !defined($instcodefilter)) {
-                my $unescapeInstcode = &unescape($inst_code);
+            if ($instcodefilter ne '.') {
+                if (!$is_hash) {
+                    $unesc_val{'inst_code'} = &unescape($val{'inst_code'});
+                }
                 if ($regexp_ok) {
-                    unless (eval('$unescapeInstcode=~/$instcodefilter/')) {
+                    if (eval{$unesc_val{'inst_code'} !~ /$instcodefilter/}) {
                         $match = 0;
                     }
                 } else {
-                    unless (eval('$unescapeInstcode=~/\Q$instcodefilter\E/i')) {
+                    if (eval{$unesc_val{'inst_code'} !~ /\Q$instcodefilter\E/i}) {
                         $match = 0;
                     }
                 }
 	    }
-            unless ($ownerfilter eq '.' || !defined($ownerfilter)) {
-                my $unescapeOwner = &unescape($owner);
+            if ($ownerfilter ne '.') {
+                if (!$is_hash) {
+                    $unesc_val{'owner'} = &unescape($val{'owner'});
+                }
                 if (($ownerunamefilter ne '') && ($ownerdomfilter ne '')) {
-                    if ($unescapeOwner =~ /:/) {
-                        if (eval('$unescapeOwner !~ 
-                             /\Q$ownerunamefilter\E:\Q$ownerdomfilter\E$/i')) {
+                    if ($unesc_val{'owner'} =~ /:/) {
+                        if (eval{$unesc_val{'owner'} !~ 
+                             /\Q$ownerunamefilter\E:\Q$ownerdomfilter\E$/i}) {
                             $match = 0;
                         } 
                     } else {
-                        if (eval('$unescapeOwner!~/\Q$ownerunamefilter\E/i')) {
+                        if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
                             $match = 0;
                         }
                     }
                 } elsif ($ownerunamefilter ne '') {
-                    if ($unescapeOwner =~ /:/) {
-                        if (eval('$unescapeOwner!~/\Q$ownerunamefilter\E:[^:]+$/i')) {
+                    if ($unesc_val{'owner'} =~ /:/) {
+                        if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E:[^:]+$/i}) {
                              $match = 0;
                         }
                     } else {
-                        if (eval('$unescapeOwner!~/\Q$ownerunamefilter\E/i')) {
+                        if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
                             $match = 0;
                         }
                     }
                 } elsif ($ownerdomfilter ne '') {
-                    if ($unescapeOwner =~ /:/) {
-                        if (eval('$unescapeOwner!~/^[^:]+:\Q$ownerdomfilter\E/')) {
+                    if ($unesc_val{'owner'} =~ /:/) {
+                        if (eval{$unesc_val{'owner'} !~ /^[^:]+:\Q$ownerdomfilter\E/}) {
                              $match = 0;
                         }
                     } else {
@@ -3455,31 +3580,53 @@ sub dump_course_id_handler {
                     }
                 }
             }
-            unless ($coursefilter eq '.' || !defined($coursefilter)) {
-                my $unescapeCourse = &unescape($key);
-                unless (eval('$unescapeCourse=~/^$udom(_)\Q$coursefilter\E$/')) {
+            if ($coursefilter ne '.') {
+                if (eval{$unesc_key !~ /^$udom(_)\Q$coursefilter\E$/}) {
                     $match = 0;
                 }
             }
-            unless ($typefilter eq '.' || !defined($typefilter)) {
-                my $unescapeType = &unescape($type);
-                if ($type eq '') {
+            if ($typefilter ne '.') {
+                if (!$is_hash) {
+                    $unesc_val{'type'} = &unescape($val{'type'});
+                }
+                if ($unesc_val{'type'} eq '') {
                     if ($typefilter ne 'Course') {
                         $match = 0;
                     }
-                } else { 
-                    unless (eval('$unescapeType=~/^\Q$typefilter\E$/')) {
+                } else {
+                    if (eval{$unesc_val{'type'} !~ /^\Q$typefilter\E$/}) {
                         $match = 0;
                     }
                 }
             }
             if ($match == 1) {
-                $qresult.=$key.'='.$descr.':'.$inst_code.':'.$owner.'&';
+                if ($rtn_as_hash) {
+                    if ($is_hash) {
+                        $qresult.=$key.'='.$value.'&';
+                    } else {
+                        my %rtnhash = ( 'description' => &unescape($val{'descr'}),
+                                        'inst_code' => &unescape($val{'inst_code'}),
+                                        'owner'     => &unescape($val{'owner'}),
+                                        'type'      => &unescape($val{'type'}),
+                                      );
+                        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'}.'&';
+                    }
+                }
             }
 	}
 	if (&untie_domain_hash($hashref)) {
 	    chop($qresult);
-	    &Reply($client, "$qresult\n", $userinput);
+	    &Reply($client, \$qresult, $userinput);
 	} else {
 	    &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
 		    "while attempting courseiddump\n", $userinput);
@@ -3488,8 +3635,6 @@ sub dump_course_id_handler {
 	&Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
 		"while attempting courseiddump\n", $userinput);
     }
-
-
     return 1;
 }
 &register_handler("courseiddump", \&dump_course_id_handler, 0, 1, 0);
@@ -3572,7 +3717,7 @@ sub get_domain_handler {
         }
         if (&untie_domain_hash($hashref)) {
             $qresult=~s/\&$//;
-            &Reply($client, "$qresult\n", $userinput);
+            &Reply($client, \$qresult, $userinput);
         } else {
             &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
                       "while attempting getdom\n",$userinput);
@@ -3670,7 +3815,7 @@ sub get_id_handler {
 	}
 	if (&untie_domain_hash($hashref)) {
 	    $qresult=~s/\&$//;
-	    &Reply($client, "$qresult\n", $userinput);
+	    &Reply($client, \$qresult, $userinput);
 	} else {
 	    &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
 		      "while attempting idget\n",$userinput);
@@ -3794,7 +3939,7 @@ sub dump_dcmail_handler {
         }
         if (&untie_domain_hash($hashref)) {
             chop($qresult);
-            &Reply($client, "$qresult\n", $userinput);
+            &Reply($client, \$qresult, $userinput);
         } else {
             &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
                     "while attempting dcmaildump\n", $userinput);
@@ -3922,7 +4067,7 @@ sub dump_domainroles_handler {
         }
         if (&untie_domain_hash($hashref)) {
             chop($qresult);
-            &Reply($client, "$qresult\n", $userinput);
+            &Reply($client, \$qresult, $userinput);
         } else {
             &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
                     "while attempting domrolesdump\n", $userinput);
@@ -3976,7 +4121,7 @@ sub tmp_put_handler {
     if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
 	print $store $record;
 	close $store;
-	&Reply($client, "$id\n", $userinput);
+	&Reply($client, \$id, $userinput);
     } else {
 	&Failure( $client, "error: ".($!+0)."IO::File->new Failed ".
 		  "while attempting tmpput\n", $userinput);
@@ -4010,7 +4155,7 @@ sub tmp_get_handler {
     my $execdir=$perlvar{'lonDaemons'};
     if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
 	my $reply=<$store>;
-	&Reply( $client, "$reply\n", $userinput);
+	&Reply( $client, \$reply, $userinput);
 	close $store;
     } else {
 	&Failure( $client, "error: ".($!+0)."IO::File->new Failed ".
@@ -4194,7 +4339,7 @@ sub enrollment_enabled_handler {
     my ($cdom) = split(/:/, $tail, 2);   # Domain we're asking about.
 
     my $outcome  = &localenroll::run($cdom);
-    &Reply($client, "$outcome\n", $userinput);
+    &Reply($client, \$outcome, $userinput);
 
     return 1;
 }
@@ -4221,7 +4366,7 @@ sub get_sections_handler {
     my @secs = &localenroll::get_sections($coursecode,$cdom);
     my $seclist = &escape(join(':',@secs));
 
-    &Reply($client, "$seclist\n", $userinput);
+    &Reply($client, \$seclist, $userinput);
     
 
     return 1;
@@ -4250,7 +4395,7 @@ sub validate_course_owner_handler {
 
     $owner = &unescape($owner);
     my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom);
-    &Reply($client, "$outcome\n", $userinput);
+    &Reply($client, \$outcome, $userinput);
 
 
 
@@ -4281,7 +4426,7 @@ sub validate_course_section_handler {
     my ($inst_course_id, $cdom) = split(/:/, $tail);
 
     my $outcome=&localenroll::validate_courseID($inst_course_id,$cdom);
-    &Reply($client, "$outcome\n", $userinput);
+    &Reply($client, \$outcome, $userinput);
 
 
     return 1;
@@ -4308,14 +4453,14 @@ sub validate_course_section_handler {
 sub validate_class_access_handler {
     my ($cmd, $tail, $client) = @_;
     my $userinput = "$cmd:$tail";
-    my ($inst_class,$courseowner,$cdom) = split(/:/, $tail);
-    $courseowner = &unescape($courseowner);
+    my ($inst_class,$ownerlist,$cdom) = split(/:/, $tail);
+    my @owners = split(/,/,&unescape($ownerlist));
     my $outcome;
     eval {
 	local($SIG{__DIE__})='DEFAULT';
-	$outcome=&localenroll::check_section($inst_class,$courseowner,$cdom);
+	$outcome=&localenroll::check_section($inst_class,\@owners,$cdom);
     };
-    &Reply($client,"$outcome\n", $userinput);
+    &Reply($client,\$outcome, $userinput);
 
     return 1;
 }
@@ -4476,7 +4621,7 @@ sub get_institutional_defaults_handler {
                 $result.=&escape($key).'='.&escape($value).'&';
             }
             $result .= 'code_order='.&escape(join('&',@code_order));
-            &Reply($client,$result."\n",$userinput);
+            &Reply($client,\$result,$userinput);
         } else {
             &Reply($client,"error\n", $userinput);
         }
@@ -4487,6 +4632,70 @@ sub get_institutional_defaults_handler {
 &register_handler("autoinstcodedefaults",
                   \&get_institutional_defaults_handler,0,1,0);
 
+sub get_institutional_user_rules {
+    my ($cmd, $tail, $client)   = @_;
+    my $userinput               = "$cmd:$tail";
+    my $dom = &unescape($tail);
+    my (%rules_hash,@rules_order);
+    my $outcome;
+    eval {
+        local($SIG{__DIE__})='DEFAULT';
+        $outcome = &localenroll::username_rules($dom,\%rules_hash,\@rules_order);
+    };
+    if (!$@) {
+        if ($outcome eq 'ok') {
+            my $result;
+            foreach my $key (keys(%rules_hash)) {
+                $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($rules_hash{$key}).'&';
+            }
+            $result =~ s/\&$//;
+            $result .= ':';
+            if (@rules_order > 0) {
+                foreach my $item (@rules_order) {
+                    $result .= &escape($item).'&';
+                }
+            }
+            $result =~ s/\&$//;
+            &Reply($client,\$result,$userinput);
+        } else {
+            &Reply($client,"error\n", $userinput);
+        }
+    } else {
+        &Failure($client,"unknown_cmd\n",$userinput);
+    }
+}
+&register_handler("instuserrules",\&get_institutional_user_rules,0,1,0);
+
+
+sub institutional_username_check {
+    my ($cmd, $tail, $client)   = @_;
+    my $userinput               = "$cmd:$tail";
+    my %rulecheck;
+    my $outcome;
+    my ($udom,$uname,@rules) = split(/:/,$tail);
+    $udom = &unescape($udom);
+    $uname = &unescape($uname);
+    @rules = map {&unescape($_);} (@rules);
+    eval {
+        local($SIG{__DIE__})='DEFAULT';
+        $outcome = &localenroll::username_check($udom,$uname,\@rules,\%rulecheck);
+    };
+    if (!$@) {
+        if ($outcome eq 'ok') {
+            my $result='';
+            foreach my $key (keys(%rulecheck)) {
+                $result.=&escape($key).'='.&Apache::lonnet::freeze_escape($rulecheck{$key}).'&';
+            }
+            &Reply($client,\$result,$userinput);
+        } else {
+            &Reply($client,"error\n", $userinput);
+        }
+    } else {
+        &Failure($client,"unknown_cmd\n",$userinput);
+    }
+}
+&register_handler("instrulecheck",\&institutional_username_check,0,1,0);
+
 
 # Get domain specific conditions for import of student photographs to a course
 #
@@ -4619,8 +4828,12 @@ sub inst_usertypes_handler {
     my ($cmd, $domain, $client) = @_;
     my $res;
     my $userinput = $cmd.":".$domain; # For logging purposes.
-    my (%typeshash,@order);  
-    if (&localenroll::inst_usertypes($domain,\%typeshash,\@order) eq 'ok') {
+    my (%typeshash,@order,$result);
+    eval {
+	local($SIG{__DIE__})='DEFAULT';
+	$result=&localenroll::inst_usertypes($domain,\%typeshash,\@order);
+    };
+    if ($result eq 'ok') {
         if (keys(%typeshash) > 0) {
             foreach my $key (keys(%typeshash)) {
                 $res.=&escape($key).'='.&escape($typeshash{$key}).'&';
@@ -4635,7 +4848,7 @@ sub inst_usertypes_handler {
         }
         $res=~s/\&$//;
     }
-    &Reply($client, "$res\n", $userinput);
+    &Reply($client, \$res, $userinput);
     return 1;
 }
 &register_handler("inst_usertypes", \&inst_usertypes_handler, 0, 1, 0);
@@ -4903,7 +5116,7 @@ sub catchexception {
     $SIG{__DIE__}='DEFAULT';
     &status("Catching exception");
     &logthis("<font color='red'>CRITICAL: "
-     ."ABNORMAL EXIT. Child $$ for server $thisserver died through "
+     ."ABNORMAL EXIT. Child $$ for server ".$perlvar{'lonHostID'}." died through "
      ."a crash with this error msg->[$error]</font>");
     &logthis('Famous last words: '.$status.' - '.$lastlog);
     if ($client) { print $client "error: $error\n"; }
@@ -5044,12 +5257,12 @@ sub UpdateHosts {
     #  either dropped or changed hosts.  Note that the re-read of the table
     #  will take care of new and changed hosts as connections come into being.
 
-    #FIXME need a way to tell lonnet that it needs to reset host
-    #cached host info
+    &Apache::lonnet::reset_hosts_info();
 
     foreach my $child (keys(%children)) {
 	my $childip = $children{$child};
-	if (defined(&Apache::lonnet::get_hosts_from_ip($childip))) {
+	if ($childip ne '127.0.0.1'
+	    && !defined(&Apache::lonnet::get_hosts_from_ip($childip))) {
 	    logthis('<font color="blue"> UpdateHosts killing child '
 		    ." $child for ip $childip </font>");
 	    kill('INT', $child);
@@ -5134,9 +5347,14 @@ sub Debug {
 #
 sub Reply {
     my ($fd, $reply, $request) = @_;
-    print $fd $reply;
-    Debug("Request was $request  Reply was $reply");
-
+    if (ref($reply)) {
+	print $fd $$reply;
+	print $fd "\n";
+	if ($DEBUG) { Debug("Request was $request  Reply was $$reply"); }
+    } else {
+	print $fd $reply;
+	if ($DEBUG) { Debug("Request was $request  Reply was $reply"); }
+    }
     $Transactions++;
 }
 
@@ -5367,8 +5585,6 @@ sub make_new_child {
 # -----------------------------------------------------------------------------
 	# see if we know client and 'check' for spoof IP by ineffective challenge
 
-	ReadManagerTable;	# May also be a manager!!
-	
 	my $outsideip=$clientip;
 	if ($clientip eq '127.0.0.1') {
 	    $outsideip=&Apache::lonnet::get_host_ip($perlvar{'lonHostID'});
@@ -5488,7 +5704,7 @@ sub make_new_child {
 # ---------------- New known client connecting, could mean machine online again
 	    if (&Apache::lonnet::get_host_ip($currenthostid) ne $clientip 
 		&& $clientip ne '127.0.0.1') {
-		&Apache::lonnet::reconlonc();
+		&Apache::lonnet::reconlonc($clientname);
 	    }
 	    &logthis("<font color='green'>Established connection: $clientname</font>");
 	    &status('Will listen to '.$clientname);
@@ -5772,6 +5988,10 @@ sub validate_user {
 									 $password,
 									 $credentials);
 		$validated = ($krbreturn == 1);
+		if (!$validated) {
+		    &logthis('krb5: '.$user.', '.$contentpwd.', '.
+			     &Authen::Krb5::error());
+		}
 	    } else {
 		$validated = 0;
 	    }
@@ -6017,7 +6237,7 @@ sub subscribe {
                 # the metadata
 		unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); }
 		$fname=~s/\/home\/httpd\/html\/res/raw/;
-		$fname="http://$thisserver/".$fname;
+		$fname="http://".&Apache::lonnet::hostname($perlvar{'lonHostID'})."/".$fname;
 		$result="$fname\n";
 	    }
 	} else {