Diff for /loncom/lond between versions 1.166 and 1.221

version 1.166, 2003/12/22 11:03:37 version 1.221, 2004/08/02 20:59:46
Line 10 Line 10
 #  #
 # LON-CAPA is free software; you can redistribute it and/or modify  # LON-CAPA is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by  # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or  # the Free Software Foundation; either version 2 of the License, or 
 # (at your option) any later version.  # (at your option) any later version.
 #  #
 # LON-CAPA is distributed in the hope that it will be useful,  # LON-CAPA is distributed in the hope that it will be useful,
Line 45  use Authen::Krb4; Line 45  use Authen::Krb4;
 use Authen::Krb5;  use Authen::Krb5;
 use lib '/home/httpd/lib/perl/';  use lib '/home/httpd/lib/perl/';
 use localauth;  use localauth;
   use localenroll;
 use File::Copy;  use File::Copy;
   use LONCAPA::ConfigFileEdit;
   use LONCAPA::lonlocal;
   use LONCAPA::lonssl;
   use Fcntl qw(:flock);
   
 my $DEBUG = 0;       # Non zero to enable debug log entries.  my $DEBUG = 0;       # Non zero to enable debug log entries.
   
Line 54  my $lastlog=''; Line 59  my $lastlog='';
   
 my $VERSION='$Revision$'; #' stupid emacs  my $VERSION='$Revision$'; #' stupid emacs
 my $remoteVERSION;  my $remoteVERSION;
 my $currenthostid;  my $currenthostid="default";
 my $currentdomainid;  my $currentdomainid;
   
 my $client;  my $client;
 my $clientip;  my $clientip; # IP address of client.
 my $clientname;  my $clientdns; # DNS name of client.
   my $clientname; # LonCAPA name of client.
   
 my $server;  my $server;
 my $thisserver;  my $thisserver; # DNS of us.
   
   my $keymode;
   
   my $cipher; # Cipher key negotiated with client
   my $tmpsnum = 0; # Id of tmpputs.
   
 #   # 
 #   Connection type is:  #   Connection type is:
Line 73  my $thisserver; Line 84  my $thisserver;
   
 my $ConnectionType;  my $ConnectionType;
   
 my %hostid;  my %hostid; # ID's for hosts in cluster by ip.
 my %hostdom;  my %hostdom; # LonCAPA domain for hosts in cluster.
 my %hostip;  my %hostip; # IPs for hosts in cluster.
   my %hostdns; # ID's of hosts looked up by DNS name.
   
 my %managers; # Ip -> manager names  my %managers; # Ip -> manager names
   
 my %perlvar; # Will have the apache conf defined perl vars.  my %perlvar; # Will have the apache conf defined perl vars.
   
 #  #
   #   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:
   #          A reference to a sub that executes the request corresponding to the keyword.
   #          A flag that is true if the request must be encoded to be acceptable.
   #          A mask with bits as follows:
   #                      CLIENT_OK    - Set when the function is allowed by ordinary clients
   #                      MANAGER_OK   - Set when the function is allowed to manager clients.
   #
   my $CLIENT_OK  = 1;
   my $MANAGER_OK = 2;
   my %Dispatcher;
   
   
   #
 #  The array below are password error strings."  #  The array below are password error strings."
 #  #
 my $lastpwderror    = 13; # Largest error number from lcpasswd.  my $lastpwderror    = 13; # Largest error number from lcpasswd.
Line 119  my @adderrors    = ("ok", Line 145  my @adderrors    = ("ok",
     "lcuseradd Password mismatch");      "lcuseradd Password mismatch");
   
   
   
   #
   #   Statistics that are maintained and dislayed in the status line.
   #
   my $Transactions = 0; # Number of attempted transactions.
   my $Failures     = 0; # Number of transcations failed.
   
   #   ResetStatistics: 
   #      Resets the statistics counters:
   #
   sub ResetStatistics {
       $Transactions = 0;
       $Failures     = 0;
   }
   
   
   
   #------------------------------------------------------------------------
   #
   #   LocalConnection
   #     Completes the formation of a locally authenticated connection.
   #     This function will ensure that the 'remote' client is really the
   #     local host.  If not, the connection is closed, and the function fails.
   #     If so, initcmd is parsed for the name of a file containing the
   #     IDEA session key.  The fie is opened, read, deleted and the session
   #     key returned to the caller.
   #
   # Parameters:
   #   $Socket      - Socket open on client.
   #   $initcmd     - The full text of the init command.
   #
   # Implicit inputs:
   #    $clientdns  - The DNS name of the remote client.
   #    $thisserver - Our DNS name.
   #
   # Returns:
   #     IDEA session key on success.
   #     undef on failure.
   #
   sub LocalConnection {
       my ($Socket, $initcmd) = @_;
       Debug("Attempting local connection: $initcmd client: $clientdns me: $thisserver");
       if($clientdns ne $thisserver) {
    &logthis('<font color="red"> LocalConnection rejecting non local: '
    ."$clientdns ne $thisserver </font>");
    close $Socket;
    return undef;
       } 
       else {
    chomp($initcmd); # Get rid of \n in filename.
    my ($init, $type, $name) = split(/:/, $initcmd);
    Debug(" Init command: $init $type $name ");
   
    # Require that $init = init, and $type = local:  Otherwise
    # the caller is insane:
   
    if(($init ne "init") && ($type ne "local")) {
       &logthis('<font color = "red"> LocalConnection: caller is insane! '
        ."init = $init, and type = $type </font>");
       close($Socket);;
       return undef;
   
    }
    #  Now get the key filename:
   
    my $IDEAKey = lonlocal::ReadKeyFile($name);
    return $IDEAKey;
       }
   }
   #------------------------------------------------------------------------------
   #
   #  SSLConnection
   #   Completes the formation of an ssh authenticated connection. The
   #   socket is promoted to an ssl socket.  If this promotion and the associated
   #   certificate exchange are successful, the IDEA key is generated and sent
   #   to the remote peer via the SSL tunnel. The IDEA key is also returned to
   #   the caller after the SSL tunnel is torn down.
   #
   # Parameters:
   #   Name              Type             Purpose
   #   $Socket          IO::Socket::INET  Plaintext socket.
   #
   # Returns:
   #    IDEA key on success.
   #    undef on failure.
   #
   sub SSLConnection {
       my $Socket   = shift;
   
       Debug("SSLConnection: ");
       my $KeyFile         = lonssl::KeyFile();
       if(!$KeyFile) {
    my $err = lonssl::LastError();
    &logthis("<font color=\"red\"> CRITICAL"
    ."Can't get key file $err </font>");
    return undef;
       }
       my ($CACertificate,
    $Certificate) = lonssl::CertificateFile();
   
   
       # If any of the key, certificate or certificate authority 
       # certificate filenames are not defined, this can't work.
   
       if((!$Certificate) || (!$CACertificate)) {
    my $err = lonssl::LastError();
    &logthis("<font color=\"red\"> CRITICAL"
    ."Can't get certificates: $err </font>");
   
    return undef;
       }
       Debug("Key: $KeyFile CA: $CACertificate Cert: $Certificate");
   
       # Indicate to our peer that we can procede with
       # a transition to ssl authentication:
   
       print $Socket "ok:ssl\n";
   
       Debug("Approving promotion -> ssl");
       #  And do so:
   
       my $SSLSocket = lonssl::PromoteServerSocket($Socket,
    $CACertificate,
    $Certificate,
    $KeyFile);
       if(! ($SSLSocket) ) { # SSL socket promotion failed.
    my $err = lonssl::LastError();
    &logthis("<font color=\"red\"> CRITICAL "
    ."SSL Socket promotion failed: $err </font>");
    return undef;
       }
       Debug("SSL Promotion successful");
   
       # 
       #  The only thing we'll use the socket for is to send the IDEA key
       #  to the peer:
   
       my $Key = lonlocal::CreateCipherKey();
       print $SSLSocket "$Key\n";
   
       lonssl::Close($SSLSocket); 
   
       Debug("Key exchange complete: $Key");
   
       return $Key;
   }
   #
   #     InsecureConnection: 
   #        If insecure connections are allowd,
   #        exchange a challenge with the client to 'validate' the
   #        client (not really, but that's the protocol):
   #        We produce a challenge string that's sent to the client.
   #        The client must then echo the challenge verbatim to us.
   #
   #  Parameter:
   #      Socket      - Socket open on the client.
   #  Returns:
   #      1           - success.
   #      0           - failure (e.g.mismatch or insecure not allowed).
   #
   sub InsecureConnection {
       my $Socket  =  shift;
   
       #   Don't even start if insecure connections are not allowed.
   
       if(! $perlvar{londAllowInsecure}) { # Insecure connections not allowed.
    return 0;
       }
   
       #   Fabricate a challenge string and send it..
   
       my $challenge = "$$".time; # pid + time.
       print $Socket "$challenge\n";
       &status("Waiting for challenge reply");
   
       my $answer = <$Socket>;
       $answer    =~s/\W//g;
       if($challenge eq $answer) {
    return 1;
       } 
       else {
    logthis("<font color='blue'>WARNING client did not respond to challenge</font>");
    &status("No challenge reqply");
    return 0;
       }
       
   
   }
   
 #  #
 #   GetCertificate: Given a transaction that requires a certificate,  #   GetCertificate: Given a transaction that requires a certificate,
 #   this function will extract the certificate from the transaction  #   this function will extract the certificate from the transaction
Line 174  sub ReadManagerTable { Line 389  sub ReadManagerTable {
    while(my $host = <MANAGERS>) {     while(my $host = <MANAGERS>) {
       chomp($host);        chomp($host);
       if ($host =~ "^#") {                  # Comment line.        if ($host =~ "^#") {                  # Comment line.
          logthis('<font color="green"> Skipping line: '. "$host</font>\n");  
          next;           next;
       }        }
       if (!defined $hostip{$host}) { # This is a non cluster member        if (!defined $hostip{$host}) { # This is a non cluster member
Line 224  sub ValidManager { Line 438  sub ValidManager {
 #     1   - Success.  #     1   - Success.
 #  #
 sub CopyFile {  sub CopyFile {
     my $oldfile = shift;  
     my $newfile = shift;      my ($oldfile, $newfile) = @_;
   
     #  The file must exist:      #  The file must exist:
   
Line 325  sub AdjustHostContents { Line 539  sub AdjustHostContents {
 #      0       - failure and $! has an errno.  #      0       - failure and $! has an errno.
 #  #
 sub InstallFile {  sub InstallFile {
     my $Filename = shift;  
     my $Contents = shift;      my ($Filename, $Contents) = @_;
     my $TempFile = $Filename.".tmp";      my $TempFile = $Filename.".tmp";
   
     #  Open the file for write:      #  Open the file for write:
Line 350  sub InstallFile { Line 564  sub InstallFile {
     return 1;      return 1;
 }  }
   
   
   #
   #   ConfigFileFromSelector: converts a configuration file selector
   #                 (one of host or domain at this point) into a 
   #                 configuration file pathname.
   #
   #  Parameters:
   #      selector  - Configuration file selector.
   #  Returns:
   #      Full path to the file or undef if the selector is invalid.
   #
   sub ConfigFileFromSelector {
       my $selector   = shift;
       my $tablefile;
   
       my $tabledir = $perlvar{'lonTabDir'}.'/';
       if ($selector eq "hosts") {
    $tablefile = $tabledir."hosts.tab";
       } elsif ($selector eq "domain") {
    $tablefile = $tabledir."domain.tab";
       } else {
    return undef;
       }
       return $tablefile;
   
   }
 #  #
 #   PushFile:  Called to do an administrative push of a file.  #   PushFile:  Called to do an administrative push of a file.
 #              - Ensure the file being pushed is one we support.  #              - Ensure the file being pushed is one we support.
Line 379  sub PushFile { Line 619  sub PushFile {
     # part of some elaborate spoof that managed somehow to authenticate.      # part of some elaborate spoof that managed somehow to authenticate.
     #      #
   
     my $tablefile = $perlvar{'lonTabDir'}.'/'; # need to precede with dir.  
     if ($filename eq "host") {      my $tablefile = ConfigFileFromSelector($filename);
  $tablefile .= "hosts.tab";      if(! (defined $tablefile)) {
     } elsif ($filename eq "domain") {  
  $tablefile .= "domain.tab";  
     } else {  
  return "refused";   return "refused";
     }      }
     #      #
Line 476  sub ReinitProcess { Line 713  sub ReinitProcess {
     }      }
     return 'ok';      return 'ok';
 }  }
   #   Validate a line in a configuration file edit script:
   #   Validation includes:
   #     - Ensuring the command is valid.
   #     - Ensuring the command has sufficient parameters
   #   Parameters:
   #     scriptline - A line to validate (\n has been stripped for what it's worth).
   #
   #   Return:
   #      0     - Invalid scriptline.
   #      1     - Valid scriptline
   #  NOTE:
   #     Only the command syntax is checked, not the executability of the
   #     command.
   #
   sub isValidEditCommand {
       my $scriptline = shift;
   
       #   Line elements are pipe separated:
   
       my ($command, $key, $newline)  = split(/\|/, $scriptline);
       &logthis('<font color="green"> isValideditCommand checking: '.
        "Command = '$command', Key = '$key', Newline = '$newline' </font>\n");
       
       if ($command eq "delete") {
    #
    #   key with no newline.
    #
    if( ($key eq "") || ($newline ne "")) {
       return 0; # Must have key but no newline.
    } else {
       return 1; # Valid syntax.
    }
       } elsif ($command eq "replace") {
    #
    #   key and newline:
    #
    if (($key eq "") || ($newline eq "")) {
       return 0;
    } else {
       return 1;
    }
       } elsif ($command eq "append") {
    if (($key ne "") && ($newline eq "")) {
       return 1;
    } else {
       return 0;
    }
       } else {
    return 0; # Invalid command.
       }
       return 0; # Should not get here!!!
   }
   #
   #   ApplyEdit - Applies an edit command to a line in a configuration 
   #               file.  It is the caller's responsiblity to validate the
   #               edit line.
   #   Parameters:
   #      $directive - A single edit directive to apply.  
   #                   Edit directives are of the form:
   #                  append|newline      - Appends a new line to the file.
   #                  replace|key|newline - Replaces the line with key value 'key'
   #                  delete|key          - Deletes the line with key value 'key'.
   #      $editor   - A config file editor object that contains the
   #                  file being edited.
   #
   sub ApplyEdit {
   
       my ($directive, $editor) = @_;
   
       # Break the directive down into its command and its parameters
       # (at most two at this point.  The meaning of the parameters, if in fact
       #  they exist depends on the command).
   
       my ($command, $p1, $p2) = split(/\|/, $directive);
   
       if($command eq "append") {
    $editor->Append($p1);          # p1 - key p2 null.
       } elsif ($command eq "replace") {
    $editor->ReplaceLine($p1, $p2);   # p1 - key p2 = newline.
       } elsif ($command eq "delete") {
    $editor->DeleteLine($p1);         # p1 - key p2 null.
       } else {          # Should not get here!!!
    die "Invalid command given to ApplyEdit $command"
       }
   }
   #
   # AdjustOurHost:
   #           Adjusts a host file stored in a configuration file editor object
   #           for the true IP address of this host. This is necessary for hosts
   #           that live behind a firewall.
   #           Those hosts have a publicly distributed IP of the firewall, but
   #           internally must use their actual IP.  We assume that a given
   #           host only has a single IP interface for now.
   # Formal Parameters:
   #     editor   - The configuration file editor to adjust.  This
   #                editor is assumed to contain a hosts.tab file.
   # Strategy:
   #    - Figure out our hostname.
   #    - Lookup the entry for this host.
   #    - Modify the line to contain our IP
   #    - Do a replace for this host.
   sub AdjustOurHost {
       my $editor        = shift;
   
       # figure out who I am.
   
       my $myHostName    = $perlvar{'lonHostID'}; # LonCAPA hostname.
   
       #  Get my host file entry.
   
       my $ConfigLine    = $editor->Find($myHostName);
       if(! (defined $ConfigLine)) {
    die "AdjustOurHost - no entry for me in hosts file $myHostName";
       }
       # figure out my IP:
       #   Use the config line to get my hostname.
       #   Use gethostbyname to translate that into an IP address.
       #
       my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon) = split(/:/,$ConfigLine);
       my $BinaryIp = gethostbyname($name);
       my $ip       = inet_ntoa($ip);
       #
       #  Reassemble the config line from the elements in the list.
       #  Note that if the loncnew items were not present before, they will
       #  be now even if they would be empty
       #
       my $newConfigLine = $id;
       foreach my $item ($domain, $role, $name, $ip, $maxcon, $idleto, $mincon) {
    $newConfigLine .= ":".$item;
       }
       #  Replace the line:
   
       $editor->ReplaceLine($id, $newConfigLine);
       
   }
   #
   #   ReplaceConfigFile:
   #              Replaces a configuration file with the contents of a
   #              configuration file editor object.
   #              This is done by:
   #              - Copying the target file to <filename>.old
   #              - Writing the new file to <filename>.tmp
   #              - Moving <filename.tmp>  -> <filename>
   #              This laborious process ensures that the system is never without
   #              a configuration file that's at least valid (even if the contents
   #              may be dated).
   #   Parameters:
   #        filename   - Name of the file to modify... this is a full path.
   #        editor     - Editor containing the file.
   #
   sub ReplaceConfigFile {
       
       my ($filename, $editor) = @_;
   
       CopyFile ($filename, $filename.".old");
   
       my $contents  = $editor->Get(); # Get the contents of the file.
   
       InstallFile($filename, $contents);
   }
   #   
   #
   #   Called to edit a configuration table  file
   #   Parameters:
   #      request           - The entire command/request sent by lonc or lonManage
   #   Return:
   #      The reply to send to the client.
   #
   sub EditFile {
       my $request = shift;
   
       #  Split the command into it's pieces:  edit:filetype:script
   
       my ($request, $filetype, $script) = split(/:/, $request,3); # : in script
   
       #  Check the pre-coditions for success:
   
       if($request != "edit") { # Something is amiss afoot alack.
    return "error:edit request detected, but request != 'edit'\n";
       }
       if( ($filetype ne "hosts")  &&
    ($filetype ne "domain")) {
    return "error:edit requested with invalid file specifier: $filetype \n";
       }
   
       #   Split the edit script and check it's validity.
   
       my @scriptlines = split(/\n/, $script);  # one line per element.
       my $linecount   = scalar(@scriptlines);
       for(my $i = 0; $i < $linecount; $i++) {
    chomp($scriptlines[$i]);
    if(!isValidEditCommand($scriptlines[$i])) {
       return "error:edit with bad script line: '$scriptlines[$i]' \n";
    }
       }
   
       #   Execute the edit operation.
       #   - Create a config file editor for the appropriate file and 
       #   - execute each command in the script:
       #
       my $configfile = ConfigFileFromSelector($filetype);
       if (!(defined $configfile)) {
    return "refused\n";
       }
       my $editor = ConfigFileEdit->new($configfile);
   
       for (my $i = 0; $i < $linecount; $i++) {
    ApplyEdit($scriptlines[$i], $editor);
       }
       # If the file is the host file, ensure that our host is
       # adjusted to have our ip:
       #
       if($filetype eq "host") {
    AdjustOurHost($editor);
       }
       #  Finally replace the current file with our file.
       #
       ReplaceConfigFile($configfile, $editor);
   
       return "ok\n";
   }
   
   #---------------------------------------------------------------
   #
   # Manipulation of hash based databases (factoring out common code
   # for later use as we refactor.
   #
   #  Ties a domain level resource file to a hash.
   #  If requested a history entry is created in the associated hist file.
   #
   #  Parameters:
   #     domain    - Name of the domain in which the resource file lives.
   #     namespace - Name of the hash within that domain.
   #     how       - How to tie the hash (e.g. GDBM_WRCREAT()).
   #     loghead   - Optional parameter, if present a log entry is created
   #                 in the associated history file and this is the first part
   #                  of that entry.
   #     logtail   - Goes along with loghead,  The actual logentry is of the
   #                 form $loghead:<timestamp>:logtail.
   # Returns:
   #    Reference to a hash bound to the db file or alternatively undef
   #    if the tie failed.
   #
   sub tie_domain_hash {
       my ($domain,$namespace,$how,$loghead,$logtail) = @_;
       
       # Filter out any whitespace in the domain name:
       
       $domain =~ s/\W//g;
       
       # We have enough to go on to tie the hash:
       
       my $user_top_dir   = $perlvar{'lonUsersDir'};
       my $domain_dir     = $user_top_dir."/$domain";
       my $resource_file  = $domain_dir."/$namespace.db";
       my %hash;
       if(tie(%hash, 'GDBM_File', $resource_file, $how, 0640)) {
    if (defined($loghead)) { # Need to log the operation.
       my $logFh = IO::File->new(">>$domain_dir/$namespace.hist");
       if($logFh) {
    my $timestamp = time;
    print $logFh "$loghead:$timestamp:$logtail\n";
       }
       $logFh->close;
    }
    return \%hash; # Return the tied hash.
       } else {
    return undef; # Tie failed.
       }
   }
   
   #
   #   Ties a user's resource file to a hash.  
   #   If necessary, an appropriate history
   #   log file entry is made as well.
   #   This sub factors out common code from the subs that manipulate
   #   the various gdbm files that keep keyword value pairs.
   # Parameters:
   #   domain       - Name of the domain the user is in.
   #   user         - Name of the 'current user'.
   #   namespace    - Namespace representing the file to tie.
   #   how          - What the tie is done to (e.g. GDBM_WRCREAT().
   #   loghead      - Optional first part of log entry if there may be a
   #                  history file.
   #   what         - Optional tail of log entry if there may be a history
   #                  file.
   # Returns:
   #   hash to which the database is tied.  It's up to the caller to untie.
   #   undef if the has could not be tied.
   #
   sub tie_user_hash {
       my ($domain,$user,$namespace,$how,$loghead,$what) = @_;
   
       $namespace=~s/\//\_/g; # / -> _
       $namespace=~s/\W//g; # whitespace eliminated.
       my $proname     = propath($domain, $user);
      
       #  Tie the database.
       
       my %hash;
       if(tie(%hash, 'GDBM_File', "$proname/$namespace.db",
      $how, 0640)) {
    # If this is a namespace for which a history is kept,
    # make the history log entry:    
    if (($namespace =~/^nohist\_/) && (defined($loghead))) {
       my $args = scalar @_;
       Debug(" Opening history: $namespace $args");
       my $hfh = IO::File->new(">>$proname/$namespace.hist"); 
       if($hfh) {
    my $now = time;
    print $hfh "$loghead:$now:$what\n";
       }
       $hfh->close;
    }
    return \%hash;
       } else {
    return undef;
       }
       
   }
   
   #--------------------- Request Handlers --------------------------------------------
   #
   #   By convention each request handler registers itself prior to the sub 
   #   declaration:
   #
   
   #++
   #
   #  Handles ping requests.
   #  Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $replyfd- File descriptor connected to the client
   #  Implicit Inputs:
   #      $currenthostid - Global variable that carries the name of the host we are
   #                       known as.
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit.
   #  Side effects:
   #      Reply information is sent to the client.
   
   sub ping_handler {
       my ($cmd, $tail, $client) = @_;
       Debug("$cmd $tail $client .. $currenthostid:");
      
       Reply( $client,"$currenthostid\n","$cmd:$tail");
      
       return 1;
   }
   &register_handler("ping", \&ping_handler, 0, 1, 1);       # Ping unencoded, client or manager.
   
   #++
   #
   # Handles pong requests.  Pong replies with our current host id, and
   #                         the results of a ping sent to us via our lonc.
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $replyfd- File descriptor connected to the client
   #  Implicit Inputs:
   #      $currenthostid - Global variable that carries the name of the host we are
   #                       connected to.
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit.
   #  Side effects:
   #      Reply information is sent to the client.
   
   sub pong_handler {
       my ($cmd, $tail, $replyfd) = @_;
   
       my $reply=&reply("ping",$clientname);
       &Reply( $replyfd, "$currenthostid:$reply\n", "$cmd:$tail"); 
       return 1;
   }
   &register_handler("pong", \&pong_handler, 0, 1, 1);       # Pong unencoded, client or manager
   
   #++
   #      Called to establish an encrypted session key with the remote client.
   #      Note that with secure lond, in most cases this function is never
   #      invoked.  Instead, the secure session key is established either
   #      via a local file that's locked down tight and only lives for a short
   #      time, or via an ssl tunnel...and is generated from a bunch-o-random
   #      bits from /dev/urandom, rather than the predictable pattern used by
   #      by this sub.  This sub is only used in the old-style insecure
   #      key negotiation.
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $replyfd- File descriptor connected to the client
   #  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.
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit.
   #  Implicit Outputs:
   #      Reply information is sent to the client.
   #      $cipher is set with a reference to a new IDEA encryption object.
   #
   sub establish_key_handler {
       my ($cmd, $tail, $replyfd) = @_;
   
       my $buildkey=time.$$.int(rand 100000);
       $buildkey=~tr/1-6/A-F/;
       $buildkey=int(rand 100000).$buildkey.int(rand 100000);
       my $key=$currenthostid.$clientname;
       $key=~tr/a-z/A-Z/;
       $key=~tr/G-P/0-9/;
       $key=~tr/Q-Z/0-9/;
       $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
       $key=substr($key,0,32);
       my $cipherkey=pack("H32",$key);
       $cipher=new IDEA $cipherkey;
       &Reply($replyfd, "$buildkey\n", "$cmd:$tail"); 
      
       return 1;
   
   }
   &register_handler("ekey", \&establish_key_handler, 0, 1,1);
   
   
   #     Handler for the load command.  Returns the current system load average
   #     to the requestor.
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $replyfd- File descriptor connected to the client
   #  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.
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit.
   #  Side effects:
   #      Reply information is sent to the client.
   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
   
       my $loadavg;
       my $loadfile=IO::File->new('/proc/loadavg');
      
       $loadavg=<$loadfile>;
       $loadavg =~ s/\s.*//g;                      # Extract the first field only.
      
       my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
   
       &Reply( $replyfd, "$loadpercent\n", "$cmd:$tail");
      
       return 1;
   }
   register_handler("load", \&load_handler, 0, 1, 0);
   
   #
   #   Process the userload request.  This sub returns to the client the current
   #  user load average.  It can be invoked either by clients or managers.
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $replyfd- File descriptor connected to the client
   #  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.
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit
   # Implicit inputs:
   #     whatever the userload() function requires.
   #  Implicit outputs:
   #     the reply is written to the client.
   #
   sub user_load_handler {
       my ($cmd, $tail, $replyfd) = @_;
   
       my $userloadpercent=&userload();
       &Reply($replyfd, "$userloadpercent\n", "$cmd:$tail");
       
       return 1;
   }
   register_handler("userload", \&user_load_handler, 0, 1, 0);
   
   #   Process a request for the authorization type of a user:
   #   (userauth).
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $replyfd- File descriptor connected to the client
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit
   # Implicit outputs:
   #    The user authorization type is written to the client.
   #
   sub user_authorization_type {
       my ($cmd, $tail, $replyfd) = @_;
      
       my $userinput = "$cmd:$tail";
      
       #  Pull the domain and username out of the command tail.
       # and call GetAuthType to determine the authentication type.
      
       my ($udom,$uname)=split(/:/,$tail);
       my $result = &GetAuthType($udom, $uname);
       if($result eq "nouser") {
    &Failure( $replyfd, "unknown_user\n", $userinput);
       } else {
    #
    # We only want to pass the second field from GetAuthType
    # for ^krb.. otherwise we'll be handing out the encrypted
    # password for internals e.g.
    #
    my ($type,$otherinfo) = split(/:/,$result);
    if($type =~ /^krb/) {
       $type = $result;
    }
    &Reply( $replyfd, "$type\n", $userinput);
       }
     
       return 1;
   }
   &register_handler("currentauth", \&user_authorization_type, 1, 1, 0);
   
   #   Process a request by a manager to push a hosts or domain table 
   #   to us.  We pick apart the command and pass it on to the subs
   #   that already exist to do this.
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $client - File descriptor connected to the client
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit
   # Implicit Output:
   #    a reply is written to the client.
   
   sub push_file_handler {
       my ($cmd, $tail, $client) = @_;
   
       my $userinput = "$cmd:$tail";
   
       # At this time we only know that the IP of our partner is a valid manager
       # the code below is a hook to do further authentication (e.g. to resolve
       # spoofing).
   
       my $cert = &GetCertificate($userinput);
       if(&ValidManager($cert)) { 
   
    # Now presumably we have the bona fides of both the peer host and the
    # process making the request.
         
    my $reply = &PushFile($userinput);
    &Reply($client, "$reply\n", $userinput);
   
       } else {
    &Failure( $client, "refused\n", $userinput);
       } 
       return 1;
   }
   &register_handler("pushfile", \&push_file_handler, 1, 0, 1);
   
   
   
   #   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.
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $client - File descriptor connected to the client
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit
   #  Implicit output:
   #     a reply is sent to the client.
   #
   sub reinit_process_handler {
       my ($cmd, $tail, $client) = @_;
      
       my $userinput = "$cmd:$tail";
      
       my $cert = &GetCertificate($userinput);
       if(&ValidManager($cert)) {
    chomp($userinput);
    my $reply = &ReinitProcess($userinput);
    &Reply( $client,  "$reply\n", $userinput);
       } else {
    &Failure( $client, "refused\n", $userinput);
       }
       return 1;
   }
   
   &register_handler("reinit", \&reinit_process_handler, 1, 0, 1);
   
   #  Process the editing script for a table edit operation.
   #  the editing operation must be encrypted and requested by
   #  a manager host.
   #
   # Parameters:
   #      $cmd    - the actual keyword that invoked us.
   #      $tail   - the tail of the request that invoked us.
   #      $client - File descriptor connected to the client
   #  Returns:
   #      1       - Ok to continue processing.
   #      0       - Program should exit
   #  Implicit output:
   #     a reply is sent to the client.
   #
   sub edit_table_handler {
       my ($command, $tail, $client) = @_;
      
       my $userinput = "$command:$tail";
   
       my $cert = &GetCertificate($userinput);
       if(&ValidManager($cert)) {
    my($filetype, $script) = split(/:/, $tail);
    if (($filetype eq "hosts") || 
       ($filetype eq "domain")) {
       if($script ne "") {
    &Reply($client,              # BUGBUG - EditFile
         &EditFile($userinput), #   could fail.
         $userinput);
       } else {
    &Failure($client,"refused\n",$userinput);
       }
    } else {
       &Failure($client,"refused\n",$userinput);
    }
       } else {
    &Failure($client,"refused\n",$userinput);
       }
       return 1;
   }
   register_handler("edit", \&edit_table_handler, 1, 0, 1);
   
   
   #
   #   Authenticate a user against the LonCAPA authentication
   #   database.  Note that there are several authentication
   #   possibilities:
   #   - unix     - The user can be authenticated against the unix
   #                password file.
   #   - internal - The user can be authenticated against a purely 
   #                internal per user password file.
   #   - kerberos - The user can be authenticated against either a kerb4 or kerb5
   #                ticket granting authority.
   #   - user     - The person tailoring LonCAPA can supply a user authentication
   #                mechanism that is per system.
   #
   # Parameters:
   #    $cmd      - The command that got us here.
   #    $tail     - Tail of the command (remaining parameters).
   #    $client   - File descriptor connected to client.
   # Returns
   #     0        - Requested to exit, caller should shut down.
   #     1        - Continue processing.
   # Implicit inputs:
   #    The authentication systems describe above have their own forms of implicit
   #    input into the authentication process that are described above.
   #
   sub authenticate_handler {
       my ($cmd, $tail, $client) = @_;
   
       
       #  Regenerate the full input line 
       
       my $userinput  = $cmd.":".$tail;
       
       #  udom    - User's domain.
       #  uname   - Username.
       #  upass   - User's password.
       
       my ($udom,$uname,$upass)=split(/:/,$tail);
       &Debug(" Authenticate domain = $udom, user = $uname, password = $upass");
       chomp($upass);
       $upass=&unescape($upass);
   
       my $pwdcorrect = &validate_user($udom, $uname, $upass);
       if($pwdcorrect) {
    &Reply( $client, "authorized\n", $userinput);
    #
    #  Bad credentials: Failed to authorize
    #
       } else {
    &Failure( $client, "non_authorized\n", $userinput);
       }
   
       return 1;
   }
   
   register_handler("auth", \&authenticate_handler, 1, 1, 0);
   
   #---------------------------------------------------------------
   #
   #   Getting, decoding and dispatching requests:
   #
   
   #
   #   Get a Request:
   #   Gets a Request message from the client.  The transaction
   #   is defined as a 'line' of text.  We remove the new line
   #   from the text line.  
   #   
   sub get_request {
       my $input = <$client>;
       chomp($input);
   
       Debug("get_request: Request = $input\n");
   
       &status('Processing '.$clientname.':'.$input);
   
       return $input;
   }
   #---------------------------------------------------------------
   #
   #  Process a request.  This sub should shrink as each action
   #  gets farmed out into a separat sub that is registered 
   #  with the dispatch hash.  
   #
   # Parameters:
   #    user_input   - The request received from the client (lonc).
   # Returns:
   #    true to keep processing, false if caller should exit.
   #
   sub process_request {
       my ($userinput) = @_;      # Easier for now to break style than to
                                   # fix all the userinput -> user_input.
       my $wasenc    = 0; # True if request was encrypted.
   # ------------------------------------------------------------ See if encrypted
       if ($userinput =~ /^enc/) {
    $userinput = decipher($userinput);
    $wasenc=1;
    if(!$userinput) { # Cipher not defined.
       &Failure($client, "error: Encrypted data without negotated key");
       return 0;
    }
       }
       Debug("process_request: $userinput\n");
       
       #  
       #   The 'correct way' to add a command to lond is now to
       #   write a sub to execute it and Add it to the command dispatch
       #   hash via a call to register_handler..  The comments to that
       #   sub should give you enough to go on to show how to do this
       #   along with the examples that are building up as this code
       #   is getting refactored.   Until all branches of the
       #   if/elseif monster below have been factored out into
       #   separate procesor subs, if the dispatch hash is missing
       #   the command keyword, we will fall through to the remainder
       #   of the if/else chain below in order to keep this thing in 
       #   working order throughout the transmogrification.
   
       my ($command, $tail) = split(/:/, $userinput, 2);
       chomp($command);
       chomp($tail);
       $tail =~ s/(\r)//; # This helps people debugging with e.g. telnet.
       $command =~ s/(\r)//; # And this too for parameterless commands.
       if(!$tail) {
    $tail =""; # defined but blank.
       }
   
       &Debug("Command received: $command, encoded = $wasenc");
   
       if(defined $Dispatcher{$command}) {
   
    my $dispatch_info = $Dispatcher{$command};
    my $handler       = $$dispatch_info[0];
    my $need_encode   = $$dispatch_info[1];
    my $client_types  = $$dispatch_info[2];
    Debug("Matched dispatch hash: mustencode: $need_encode "
         ."ClientType $client_types");
         
    #  Validate the request:
         
    my $ok = 1;
    my $requesterprivs = 0;
    if(&isClient()) {
       $requesterprivs |= $CLIENT_OK;
    }
    if(&isManager()) {
       $requesterprivs |= $MANAGER_OK;
    }
    if($need_encode && (!$wasenc)) {
       Debug("Must encode but wasn't: $need_encode $wasenc");
       $ok = 0;
    }
    if(($client_types & $requesterprivs) == 0) {
       Debug("Client not privileged to do this operation");
       $ok = 0;
    }
   
    if($ok) {
       Debug("Dispatching to handler $command $tail");
       my $keep_going = &$handler($command, $tail, $client);
       return $keep_going;
    } else {
       Debug("Refusing to dispatch because client did not match requirements");
       Failure($client, "refused\n", $userinput);
       return 1;
    }
   
       }    
   
   #------------------- Commands not yet in spearate handlers. --------------
   
   
   # ---------------------------------------------------------------------- passwd
       if ($userinput =~ /^passwd/) { # encoded and client
    if (($wasenc==1) && isClient) {
       my 
    ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);
       chomp($npass);
       $upass=&unescape($upass);
       $npass=&unescape($npass);
       &Debug("Trying to change password for $uname");
       my $proname=propath($udom,$uname);
       my $passfilename="$proname/passwd";
       if (-e $passfilename) {
    my $realpasswd;
    { my $pf = IO::File->new($passfilename);
     $realpasswd=<$pf>; }
    chomp($realpasswd);
    my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
    if ($howpwd eq 'internal') {
       &Debug("internal auth");
       if (crypt($upass,$contentpwd) eq $contentpwd) {
    my $salt=time;
    $salt=substr($salt,6,2);
    my $ncpass=crypt($npass,$salt);
    {
       my $pf;
       if ($pf = IO::File->new(">$passfilename")) {
    print $pf "internal:$ncpass\n";
    &logthis("Result of password change for $uname: pwchange_success");
    print $client "ok\n";
       } else {
    &logthis("Unable to open $uname passwd to change password");
    print $client "non_authorized\n";
       }
    }             
   
       } else {
    print $client "non_authorized\n";
       }
    } elsif ($howpwd eq 'unix') {
       # Unix means we have to access /etc/password
       # one way or another.
       # First: Make sure the current password is
       #        correct
       &Debug("auth is unix");
       $contentpwd=(getpwnam($uname))[1];
       my $pwdcorrect = "0";
       my $pwauth_path="/usr/local/sbin/pwauth";
       unless ($contentpwd eq 'x') {
    $pwdcorrect=
       (crypt($upass,$contentpwd) eq $contentpwd);
       } elsif (-e $pwauth_path) {
    open PWAUTH, "|$pwauth_path" or
       die "Cannot invoke authentication";
    print PWAUTH "$uname\n$upass\n";
    close PWAUTH;
    &Debug("exited pwauth with $? ($uname,$upass) ");
    $pwdcorrect=($? == 0);
       }
       if ($pwdcorrect) {
    my $execdir=$perlvar{'lonDaemons'};
    &Debug("Opening lcpasswd pipeline");
    my $pf = IO::File->new("|$execdir/lcpasswd > $perlvar{'lonDaemons'}/logs/lcpasswd.log");
    print $pf "$uname\n$npass\n$npass\n";
    close $pf;
    my $err = $?;
    my $result = ($err>0 ? 'pwchange_failure' 
         : 'ok');
    &logthis("Result of password change for $uname: ".
    &lcpasswdstrerror($?));
    print $client "$result\n";
       } else {
    print $client "non_authorized\n";
       }
    } else {
       print $client "auth_mode_error\n";
    }  
       } else {
    print $client "unknown_user\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # -------------------------------------------------------------------- makeuser
       } elsif ($userinput =~ /^makeuser/) { # encoded and client.
    &Debug("Make user received");
    my $oldumask=umask(0077);
    if (($wasenc==1) && isClient) {
       my 
    ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
       &Debug("cmd =".$cmd." $udom =".$udom.
      " uname=".$uname);
       chomp($npass);
       $npass=&unescape($npass);
       my $proname=propath($udom,$uname);
       my $passfilename="$proname/passwd";
       &Debug("Password file created will be:".
      $passfilename);
       if (-e $passfilename) {
    print $client "already_exists\n";
       } elsif ($udom ne $currentdomainid) {
    print $client "not_right_domain\n";
       } else {
    my @fpparts=split(/\//,$proname);
    my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];
    my $fperror='';
    for (my $i=3;$i<=$#fpparts;$i++) {
       $fpnow.='/'.$fpparts[$i]; 
       unless (-e $fpnow) {
    unless (mkdir($fpnow,0777)) {
       $fperror="error: ".($!+0)
    ." mkdir failed while attempting "
    ."makeuser";
    }
       }
    }
    unless ($fperror) {
       my $result=&make_passwd_file($uname, $umode,$npass,
    $passfilename);
       print $client $result;
    } else {
       print $client "$fperror\n";
    }
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
    umask($oldumask);
   # -------------------------------------------------------------- changeuserauth
       } elsif ($userinput =~ /^changeuserauth/) { # encoded & client
    &Debug("Changing authorization");
    if (($wasenc==1) && isClient) {
       my 
    ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
       chomp($npass);
       &Debug("cmd = ".$cmd." domain= ".$udom.
      "uname =".$uname." umode= ".$umode);
       $npass=&unescape($npass);
       my $proname=&propath($udom,$uname);
       my $passfilename="$proname/passwd";
       if ($udom ne $currentdomainid) {
    print $client "not_right_domain\n";
       } else {
    my $result=&make_passwd_file($uname, $umode,$npass,
        $passfilename);
    print $client $result;
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------ home
       } elsif ($userinput =~ /^home/) { # client clear or encoded
    if(isClient) {
       my ($cmd,$udom,$uname)=split(/:/,$userinput);
       chomp($uname);
       my $proname=propath($udom,$uname);
       if (-e $proname) {
    print $client "found\n";
       } else {
    print $client "not_found\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ---------------------------------------------------------------------- update
       } elsif ($userinput =~ /^update/) { # client clear or encoded.
    if(isClient) {
       my ($cmd,$fname)=split(/:/,$userinput);
       my $ownership=ishome($fname);
       if ($ownership eq 'not_owner') {
    if (-e $fname) {
       my ($dev,$ino,$mode,$nlink,
    $uid,$gid,$rdev,$size,
    $atime,$mtime,$ctime,
    $blksize,$blocks)=stat($fname);
       my $now=time;
       my $since=$now-$atime;
       if ($since>$perlvar{'lonExpire'}) {
    my $reply=
       &reply("unsub:$fname","$clientname");
       unlink("$fname");
       } else {
    my $transname="$fname.in.transfer";
    my $remoteurl=
       &reply("sub:$fname","$clientname");
    my $response;
    {
       my $ua=new LWP::UserAgent;
       my $request=new HTTP::Request('GET',"$remoteurl");
       $response=$ua->request($request,$transname);
    }
    if ($response->is_error()) {
       unlink($transname);
       my $message=$response->status_line;
       &logthis(
        "LWP GET: $message for $fname ($remoteurl)");
    } else {
       if ($remoteurl!~/\.meta$/) {
    my $ua=new LWP::UserAgent;
    my $mrequest=
       new HTTP::Request('GET',$remoteurl.'.meta');
    my $mresponse=
       $ua->request($mrequest,$fname.'.meta');
    if ($mresponse->is_error()) {
       unlink($fname.'.meta');
    }
       }
       rename($transname,$fname);
    }
       }
       print $client "ok\n";
    } else {
       print $client "not_found\n";
    }
       } else {
    print $client "rejected\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # -------------------------------------- fetch a user file from a remote server
       } elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc.
    if(isClient) {
       my ($cmd,$fname)=split(/:/,$userinput);
       my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|);
       my $udir=propath($udom,$uname).'/userfiles';
       unless (-e $udir) { mkdir($udir,0770); }
       if (-e $udir) {
    $ufile=~s/^[\.\~]+//;
    my $path = $udir;
    if ($ufile =~m|(.+)/([^/]+)$|) {
       my @parts=split('/',$1);
       foreach my $part (@parts) {
    $path .= '/'.$part;
    if ((-e $path)!=1) {
       mkdir($path,0770);
    }
       }
    }
    my $destname=$udir.'/'.$ufile;
    my $transname=$udir.'/'.$ufile.'.in.transit';
    my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;
    my $response;
    {
       my $ua=new LWP::UserAgent;
       my $request=new HTTP::Request('GET',"$remoteurl");
       $response=$ua->request($request,$transname);
    }
    if ($response->is_error()) {
       unlink($transname);
       my $message=$response->status_line;
       &logthis("LWP GET: $message for $fname ($remoteurl)");
       print $client "failed\n";
    } else {
       if (!rename($transname,$destname)) {
    &logthis("Unable to move $transname to $destname");
    unlink($transname);
    print $client "failed\n";
       } else {
    print $client "ok\n";
       }
    }
       } else {
    print $client "not_home\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
    }
   # --------------------------------------------------------- remove a user file 
       } elsif ($userinput =~ /^removeuserfile/) { # Client clear or enc.
    if(isClient) {
       my ($cmd,$fname)=split(/:/,$userinput);
       my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|);
       &logthis("$udom - $uname - $ufile");
       if ($ufile =~m|/\.\./|) {
    # any files paths with /../ in them refuse 
    # to deal with
    print $client "refused\n";
       } else {
    my $udir=propath($udom,$uname);
    if (-e $udir) {
       my $file=$udir.'/userfiles/'.$ufile;
       if (-e $file) {
    unlink($file);
    if (-e $file) {
       print $client "failed\n";
    } else {
       print $client "ok\n";
    }
       } else {
    print $client "not_found\n";
       }
    } else {
       print $client "not_home\n";
    }
       }
    } else {
       Reply($client, "refused\n", $userinput);
    }
   # ------------------------------------------ authenticate access to a user file
       } elsif ($userinput =~ /^tokenauthuserfile/) { # Client only
    if(isClient) {
       my ($cmd,$fname,$session)=split(/:/,$userinput);
       chomp($session);
       my $reply='non_auth';
       if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.
        $session.'.id')) {
    while (my $line=<ENVIN>) {
       if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; }
       }
    close(ENVIN);
    print $client $reply."\n";
       } else {
    print $client "invalid_token\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------- unsubscribe
       } elsif ($userinput =~ /^unsub/) {
    if(isClient) {
       my ($cmd,$fname)=split(/:/,$userinput);
       if (-e $fname) {
    print $client &unsub($fname,$clientip);
       } else {
    print $client "not_found\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------- subscribe
       } elsif ($userinput =~ /^sub/) {
    if(isClient) {
       print $client &subscribe($userinput,$clientip);
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------- current version
       } elsif ($userinput =~ /^currentversion/) {
    if(isClient) {
       my ($cmd,$fname)=split(/:/,$userinput);
       print $client &currentversion($fname)."\n";
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------- log
       } elsif ($userinput =~ /^log/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
       chomp($what);
       my $proname=propath($udom,$uname);
       my $now=time;
       {
    my $hfh;
    if ($hfh=IO::File->new(">>$proname/activity.log")) { 
       print $hfh "$now:$clientname:$what\n";
       print $client "ok\n"; 
    } else {
       print $client "error: ".($!+0)
    ." IO::File->new Failed "
    ."while attempting log\n";
    }
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------- put
       } elsif ($userinput =~ /^put/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$what)
    =split(/:/,$userinput,5);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       if ($namespace ne 'roles') {
    chomp($what);
    my $proname=propath($udom,$uname);
    my $now=time;
    my @pairs=split(/\&/,$what);
    my %hash;
    if (tie(%hash,'GDBM_File',
    "$proname/$namespace.db",
    &GDBM_WRCREAT(),0640)) {
       unless ($namespace=~/^nohist\_/) {
    my $hfh;
    if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "P:$now:$what\n"; }
       }
       
       foreach my $pair (@pairs) {
    my ($key,$value)=split(/=/,$pair);
    $hash{$key}=$value;
       }
       if (untie(%hash)) {
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)
       ." untie(GDBM) failed ".
       "while attempting put\n";
       }
    } else {
       print $client "error: ".($!)
    ." tie(GDBM) Failed ".
    "while attempting put\n";
    }
       } else {
    print $client "refused\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------- inc
       } elsif ($userinput =~ /^inc:/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$what)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       if ($namespace ne 'roles') {
    chomp($what);
    my $proname=propath($udom,$uname);
    my $now=time;
    my @pairs=split(/\&/,$what);
    my %hash;
    if (tie(%hash,'GDBM_File',
    "$proname/$namespace.db",
    &GDBM_WRCREAT(),0640)) {
       unless ($namespace=~/^nohist\_/) {
    my $hfh;
    if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "P:$now:$what\n"; }
       }
       foreach my $pair (@pairs) {
    my ($key,$value)=split(/=/,$pair);
    # We could check that we have a number...
    if (! defined($value) || $value eq '') {
       $value = 1;
    }
    $hash{$key}+=$value;
       }
       if (untie(%hash)) {
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)
       ." untie(GDBM) failed ".
       "while attempting inc\n";
       }
    } else {
       print $client "error: ".($!)
    ." tie(GDBM) Failed ".
    "while attempting inc\n";
    }
       } else {
    print $client "refused\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # -------------------------------------------------------------------- rolesput
       } elsif ($userinput =~ /^rolesput/) {
    if(isClient) {
       &Debug("rolesput");
       if ($wasenc==1) {
    my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
       =split(/:/,$userinput);
    &Debug("cmd = ".$cmd." exedom= ".$exedom.
          "user = ".$exeuser." udom=".$udom.
          "what = ".$what);
    my $namespace='roles';
    chomp($what);
    my $proname=propath($udom,$uname);
    my $now=time;
    my @pairs=split(/\&/,$what);
    my %hash;
    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
       {
    my $hfh;
    if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { 
       print $hfh "P:$now:$exedom:$exeuser:$what\n";
    }
       }
       
       foreach my $pair (@pairs) {
    my ($key,$value)=split(/=/,$pair);
    &ManagePermissions($key, $udom, $uname,
      &GetAuthType( $udom, 
    $uname));
    $hash{$key}=$value;
       }
       if (untie(%hash)) {
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)
       ." untie(GDBM) Failed ".
       "while attempting rolesput\n";
       }
    } else {
       print $client "error: ".($!+0)
    ." tie(GDBM) Failed ".
    "while attempting rolesput\n";
       }
       } else {
    print $client "refused\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # -------------------------------------------------------------------- rolesdel
       } elsif ($userinput =~ /^rolesdel/) {
    if(isClient) {
       &Debug("rolesdel");
       if ($wasenc==1) {
    my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
       =split(/:/,$userinput);
    &Debug("cmd = ".$cmd." exedom= ".$exedom.
          "user = ".$exeuser." udom=".$udom.
          "what = ".$what);
    my $namespace='roles';
    chomp($what);
    my $proname=propath($udom,$uname);
    my $now=time;
    my @rolekeys=split(/\&/,$what);
    my %hash;
    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
       {
    my $hfh;
    if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { 
       print $hfh "D:$now:$exedom:$exeuser:$what\n";
    }
       }
       foreach my $key (@rolekeys) {
    delete $hash{$key};
       }
       if (untie(%hash)) {
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)
       ." untie(GDBM) Failed ".
       "while attempting rolesdel\n";
       }
    } else {
       print $client "error: ".($!+0)
    ." tie(GDBM) Failed ".
    "while attempting rolesdel\n";
    }
       } else {
    print $client "refused\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------- get
       } elsif ($userinput =~ /^get/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$what)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       chomp($what);
       my @queries=split(/\&/,$what);
       my $proname=propath($udom,$uname);
       my $qresult='';
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
    for (my $i=0;$i<=$#queries;$i++) {
       $qresult.="$hash{$queries[$i]}&";
    }
    if (untie(%hash)) {
       $qresult=~s/\&$//;
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting get\n";
    }
       } else {
    if ($!+0 == 2) {
       print $client "error:No such file or ".
    "GDBM reported bad block error\n";
    } else {
       print $client "error: ".($!+0)
    ." tie(GDBM) Failed ".
    "while attempting get\n";
    }
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------ eget
       } elsif ($userinput =~ /^eget/) {
    if (isClient) {
       my ($cmd,$udom,$uname,$namespace,$what)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       chomp($what);
       my @queries=split(/\&/,$what);
       my $proname=propath($udom,$uname);
       my $qresult='';
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
    for (my $i=0;$i<=$#queries;$i++) {
       $qresult.="$hash{$queries[$i]}&";
    }
    if (untie(%hash)) {
       $qresult=~s/\&$//;
       if ($cipher) {
    my $cmdlength=length($qresult);
    $qresult.="         ";
    my $encqresult='';
    for 
       (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
    $encqresult.=
       unpack("H16",
      $cipher->encrypt(substr($qresult,$encidx,8)));
       }
    print $client "enc:$cmdlength:$encqresult\n";
       } else {
    print $client "error:no_key\n";
       }
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting eget\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting eget\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------- del
       } elsif ($userinput =~ /^del/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$what)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       chomp($what);
       my $proname=propath($udom,$uname);
       my $now=time;
       my @keys=split(/\&/,$what);
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
    unless ($namespace=~/^nohist\_/) {
       my $hfh;
       if ($hfh=IO::File->new(">>$proname/$namespace.hist")) { print $hfh "D:$now:$what\n"; }
    }
    foreach my $key (@keys) {
       delete($hash{$key});
    }
    if (untie(%hash)) {
       print $client "ok\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting del\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting del\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------------ keys
       } elsif ($userinput =~ /^keys/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       my $proname=propath($udom,$uname);
       my $qresult='';
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
    foreach my $key (keys %hash) {
       $qresult.="$key&";
    }
    if (untie(%hash)) {
       $qresult=~s/\&$//;
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting keys\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting keys\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------- dumpcurrent
       } elsif ($userinput =~ /^currentdump/) {
    if (isClient) {
       my ($cmd,$udom,$uname,$namespace)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       my $qresult='';
       my $proname=propath($udom,$uname);
       my %hash;
       if (tie(%hash,'GDBM_File',
       "$proname/$namespace.db",
       &GDBM_READER(),0640)) {
       # Structure of %data:
    # $data{$symb}->{$parameter}=$value;
    # $data{$symb}->{'v.'.$parameter}=$version;
    # since $parameter will be unescaped, we do not
    # have to worry about silly parameter names...
    my %data = ();
    while (my ($key,$value) = each(%hash)) {
       my ($v,$symb,$param) = split(/:/,$key);
       next if ($v eq 'version' || $symb eq 'keys');
       next if (exists($data{$symb}) && 
        exists($data{$symb}->{$param}) &&
        $data{$symb}->{'v.'.$param} > $v);
       $data{$symb}->{$param}=$value;
       $data{$symb}->{'v.'.$param}=$v;
    }
    if (untie(%hash)) {
       while (my ($symb,$param_hash) = each(%data)) {
    while(my ($param,$value) = each (%$param_hash)){
       next if ($param =~ /^v\./);
       $qresult.=$symb.':'.$param.'='.$value.'&';
    }
       }
       chop($qresult);
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting currentdump\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting currentdump\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
    }
   # ------------------------------------------------------------------------ dump
       } elsif ($userinput =~ /^dump/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$regexp)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       if (defined($regexp)) {
    $regexp=&unescape($regexp);
       } else {
    $regexp='.';
       }
       my $qresult='';
       my $proname=propath($udom,$uname);
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
    while (my ($key,$value) = each(%hash)) {
       if ($regexp eq '.') {
    $qresult.=$key.'='.$value.'&';
       } else {
    my $unescapeKey = &unescape($key);
    if (eval('$unescapeKey=~/$regexp/')) {
       $qresult.="$key=$value&";
    }
       }
    }
    if (untie(%hash)) {
       chop($qresult);
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting dump\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting dump\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------------- store
       } elsif ($userinput =~ /^store/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$rid,$what)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       if ($namespace ne 'roles') {
    chomp($what);
    my $proname=propath($udom,$uname);
    my $now=time;
    my @pairs=split(/\&/,$what);
    my %hash;
    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
       unless ($namespace=~/^nohist\_/) {
    my $hfh;
    if ($hfh=IO::File->new(">>$proname/$namespace.hist")) {
       print $hfh "P:$now:$rid:$what\n";
    }
       }
       my @previouskeys=split(/&/,$hash{"keys:$rid"});
       my $key;
       $hash{"version:$rid"}++;
       my $version=$hash{"version:$rid"};
       my $allkeys=''; 
       foreach my $pair (@pairs) {
    my ($key,$value)=split(/=/,$pair);
    $allkeys.=$key.':';
    $hash{"$version:$rid:$key"}=$value;
       }
       $hash{"$version:$rid:timestamp"}=$now;
       $allkeys.='timestamp';
       $hash{"$version:keys:$rid"}=$allkeys;
       if (untie(%hash)) {
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)
       ." untie(GDBM) Failed ".
       "while attempting store\n";
    }
    } else {
       print $client "error: ".($!+0)
    ." tie(GDBM) Failed ".
    "while attempting store\n";
    }
       } else {
    print $client "refused\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # --------------------------------------------------------------------- restore
       } elsif ($userinput =~ /^restore/) {
    if(isClient) {
       my ($cmd,$udom,$uname,$namespace,$rid)
    =split(/:/,$userinput);
       $namespace=~s/\//\_/g;
       $namespace=~s/\W//g;
       chomp($rid);
       my $proname=propath($udom,$uname);
       my $qresult='';
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
    my $version=$hash{"version:$rid"};
    $qresult.="version=$version&";
    my $scope;
    for ($scope=1;$scope<=$version;$scope++) {
       my $vkeys=$hash{"$scope:keys:$rid"};
       my @keys=split(/:/,$vkeys);
       my $key;
       $qresult.="$scope:keys=$vkeys&";
       foreach $key (@keys) {
    $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
       }                                  
    }
    if (untie(%hash)) {
       $qresult=~s/\&$//;
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting restore\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting restore\n";
       }
    } else  {
       Reply($client, "refused\n", $userinput);
       
    }
   # -------------------------------------------------------------------- chatsend
       } elsif ($userinput =~ /^chatsend/) {
    if(isClient) {
       my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput);
       &chatadd($cdom,$cnum,$newpost);
       print $client "ok\n";
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # -------------------------------------------------------------------- chatretr
       } elsif ($userinput =~ /^chatretr/) {
    if(isClient) {
       my 
    ($cmd,$cdom,$cnum,$udom,$uname)=split(/\:/,$userinput);
       my $reply='';
       foreach (&getchat($cdom,$cnum,$udom,$uname)) {
    $reply.=&escape($_).':';
       }
       $reply=~s/\:$//;
       print $client $reply."\n";
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------- querysend
       } elsif ($userinput =~ /^querysend/) {
    if (isClient) {
       my ($cmd,$query,
    $arg1,$arg2,$arg3)=split(/\:/,$userinput);
       $query=~s/\n*$//g;
       print $client "".
    sqlreply("$clientname\&$query".
    "\&$arg1"."\&$arg2"."\&$arg3")."\n";
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------ queryreply
       } elsif ($userinput =~ /^queryreply/) {
    if(isClient) {
       my ($cmd,$id,$reply)=split(/:/,$userinput); 
       my $store;
       my $execdir=$perlvar{'lonDaemons'};
       if ($store=IO::File->new(">$execdir/tmp/$id")) {
    $reply=~s/\&/\n/g;
    print $store $reply;
    close $store;
    my $store2=IO::File->new(">$execdir/tmp/$id.end");
    print $store2 "done\n";
    close $store2;
    print $client "ok\n";
       }
       else {
    print $client "error: ".($!+0)
       ." IO::File->new Failed ".
       "while attempting queryreply\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------- courseidput
       } elsif ($userinput =~ /^courseidput/) {
    if(isClient) {
       my ($cmd,$udom,$what)=split(/:/,$userinput);
       chomp($what);
    $udom=~s/\W//g;
       my $proname=
    "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
       my $now=time;
       my @pairs=split(/\&/,$what);
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
    foreach my $pair (@pairs) {
       my ($key,$descr,$inst_code)=split(/=/,$pair);
       $hash{$key}=$descr.':'.$inst_code.':'.$now;
    }
    if (untie(%hash)) {
       print $client "ok\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting courseidput\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting courseidput\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ---------------------------------------------------------------- courseiddump
       } elsif ($userinput =~ /^courseiddump/) {
    if(isClient) {
       my ($cmd,$udom,$since,$description)
    =split(/:/,$userinput);
       if (defined($description)) {
    $description=&unescape($description);
       } else {
    $description='.';
       }
       unless (defined($since)) { $since=0; }
       my $qresult='';
       my $proname=
    "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
    while (my ($key,$value) = each(%hash)) {
       my ($descr,$lasttime,$inst_code);
       if ($value =~ m/^([^\:]*):([^\:]*):(\d+)$/) {
    ($descr,$inst_code,$lasttime)=($1,$2,$3);
       } else {
    ($descr,$lasttime) = split(/\:/,$value);
       }
       if ($lasttime<$since) { next; }
       if ($description eq '.') {
    $qresult.=$key.'='.$descr.':'.$inst_code.'&';
       } else {
    my $unescapeVal = &unescape($descr);
    if (eval('$unescapeVal=~/\Q$description\E/i')) {
       $qresult.=$key.'='.$descr.':'.$inst_code.'&';
    }
       }
    }
    if (untie(%hash)) {
       chop($qresult);
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting courseiddump\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting courseiddump\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------------- idput
       } elsif ($userinput =~ /^idput/) {
    if(isClient) {
       my ($cmd,$udom,$what)=split(/:/,$userinput);
       chomp($what);
       $udom=~s/\W//g;
       my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
       my $now=time;
       my @pairs=split(/\&/,$what);
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
    {
       my $hfh;
       if ($hfh=IO::File->new(">>$proname.hist")) {
    print $hfh "P:$now:$what\n";
       }
    }
    foreach my $pair (@pairs) {
       my ($key,$value)=split(/=/,$pair);
       $hash{$key}=$value;
    }
    if (untie(%hash)) {
       print $client "ok\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting idput\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting idput\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------------- idget
       } elsif ($userinput =~ /^idget/) {
    if(isClient) {
       my ($cmd,$udom,$what)=split(/:/,$userinput);
       chomp($what);
       $udom=~s/\W//g;
       my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
       my @queries=split(/\&/,$what);
       my $qresult='';
       my %hash;
       if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
    for (my $i=0;$i<=$#queries;$i++) {
       $qresult.="$hash{$queries[$i]}&";
    }
    if (untie(%hash)) {
       $qresult=~s/\&$//;
       print $client "$qresult\n";
    } else {
       print $client "error: ".($!+0)
    ." untie(GDBM) Failed ".
    "while attempting idget\n";
    }
       } else {
    print $client "error: ".($!+0)
       ." tie(GDBM) Failed ".
       "while attempting idget\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ---------------------------------------------------------------------- tmpput
       } elsif ($userinput =~ /^tmpput/) {
    if(isClient) {
       my ($cmd,$what)=split(/:/,$userinput);
       my $store;
       $tmpsnum++;
       my $id=$$.'_'.$clientip.'_'.$tmpsnum;
       $id=~s/\W/\_/g;
       $what=~s/\n//g;
       my $execdir=$perlvar{'lonDaemons'};
       if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
    print $store $what;
    close $store;
    print $client "$id\n";
       }
       else {
    print $client "error: ".($!+0)
       ."IO::File->new Failed ".
       "while attempting tmpput\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   
   # ---------------------------------------------------------------------- tmpget
       } elsif ($userinput =~ /^tmpget/) {
    if(isClient) {
       my ($cmd,$id)=split(/:/,$userinput);
       chomp($id);
       $id=~s/\W/\_/g;
       my $store;
       my $execdir=$perlvar{'lonDaemons'};
       if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
    my $reply=<$store>;
       print $client "$reply\n";
    close $store;
       }
       else {
    print $client "error: ".($!+0)
       ."IO::File->new Failed ".
       "while attempting tmpget\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ---------------------------------------------------------------------- tmpdel
       } elsif ($userinput =~ /^tmpdel/) {
    if(isClient) {
       my ($cmd,$id)=split(/:/,$userinput);
       chomp($id);
       $id=~s/\W/\_/g;
       my $execdir=$perlvar{'lonDaemons'};
       if (unlink("$execdir/tmp/$id.tmp")) {
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)
       ."Unlink tmp Failed ".
       "while attempting tmpdel\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------- portfolio directory list (portls)
       } elsif ($userinput =~ /^portls/) {
    if(isClient) {
       my ($cmd,$uname,$udom)=split(/:/,$userinput);
       my $udir=propath($udom,$uname).'/userfiles/portfolio';
       my $dirLine='';
       my $dirContents='';
       if (opendir(LSDIR,$udir.'/')){
    while ($dirLine = readdir(LSDIR)){
       $dirContents = $dirContents.$dirLine.'<br />';
    }
       } else {
    $dirContents = "No directory found\n";
       }
       print $client $dirContents."\n";
    } else {
       Reply($client, "refused\n", $userinput);
    }
   # -------------------------------------------------------------------------- ls
       } elsif ($userinput =~ /^ls/) {
    if(isClient) {
       my $obs;
       my $rights;
       my ($cmd,$ulsdir)=split(/:/,$userinput);
       my $ulsout='';
       my $ulsfn;
       if (-e $ulsdir) {
    if(-d $ulsdir) {
       if (opendir(LSDIR,$ulsdir)) {
    while ($ulsfn=readdir(LSDIR)) {
       undef $obs, $rights; 
       my @ulsstats=stat($ulsdir.'/'.$ulsfn);
       #We do some obsolete checking here
       if(-e $ulsdir.'/'.$ulsfn.".meta") { 
    open(FILE, $ulsdir.'/'.$ulsfn.".meta");
    my @obsolete=<FILE>;
    foreach my $obsolete (@obsolete) {
       if($obsolete =~ m|(<obsolete>)(on)|) { $obs = 1; } 
       if($obsolete =~ m|(<copyright>)(default)|) { $rights = 1; }
    }
       }
       $ulsout.=$ulsfn.'&'.join('&',@ulsstats);
       if($obs eq '1') { $ulsout.="&1"; }
       else { $ulsout.="&0"; }
       if($rights eq '1') { $ulsout.="&1:"; }
       else { $ulsout.="&0:"; }
    }
    closedir(LSDIR);
       }
    } else {
       my @ulsstats=stat($ulsdir);
       $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
    }
       } else {
    $ulsout='no_such_dir';
       }
       if ($ulsout eq '') { $ulsout='empty'; }
       print $client "$ulsout\n";
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ----------------------------------------------------------------- setannounce
       } elsif ($userinput =~ /^setannounce/) {
    if (isClient) {
       my ($cmd,$announcement)=split(/:/,$userinput);
       chomp($announcement);
       $announcement=&unescape($announcement);
       if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}.
    '/announcement.txt')) {
    print $store $announcement;
    close $store;
    print $client "ok\n";
       } else {
    print $client "error: ".($!+0)."\n";
       }
    } else {
       Reply($client, "refused\n", $userinput);
       
    }
   # ------------------------------------------------------------------ Hanging up
       } elsif (($userinput =~ /^exit/) ||
        ($userinput =~ /^init/)) { # no restrictions.
    &logthis(
    "Client $clientip ($clientname) hanging up: $userinput");
    print $client "bye\n";
    $client->shutdown(2);        # shutdown the socket forcibly.
    $client->close();
    return 0;
   
   # ---------------------------------- set current host/domain
       } elsif ($userinput =~ /^sethost:/) {
    if (isClient) {
       print $client &sethost($userinput)."\n";
    } else {
       print $client "refused\n";
    }
   #---------------------------------- request file (?) version.
       } elsif ($userinput =~/^version:/) {
    if (isClient) {
       print $client &version($userinput)."\n";
    } else {
       print $client "refused\n";
    }
   #------------------------------- is auto-enrollment enabled?
       } elsif ($userinput =~/^autorun:/) {
    if (isClient) {
       my ($cmd,$cdom) = split(/:/,$userinput);
       my $outcome = &localenroll::run($cdom);
       print $client "$outcome\n";
    } else {
       print $client "0\n";
    }
   #------------------------------- get official sections (for auto-enrollment).
       } elsif ($userinput =~/^autogetsections:/) {
    if (isClient) {
       my ($cmd,$coursecode,$cdom)=split(/:/,$userinput);
       my @secs = &localenroll::get_sections($coursecode,$cdom);
       my $seclist = &escape(join(':',@secs));
       print $client "$seclist\n";
    } else {
       print $client "refused\n";
    }
   #----------------------- validate owner of new course section (for auto-enrollment).
       } elsif ($userinput =~/^autonewcourse:/) {
    if (isClient) {
       my ($cmd,$inst_course_id,$owner,$cdom)=split(/:/,$userinput);
       my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom);
       print $client "$outcome\n";
    } else {
       print $client "refused\n";
    }
   #-------------- validate course section in schedule of classes (for auto-enrollment).
       } elsif ($userinput =~/^autovalidatecourse:/) {
    if (isClient) {
       my ($cmd,$inst_course_id,$cdom)=split(/:/,$userinput);
       my $outcome=&localenroll::validate_courseID($inst_course_id,$cdom);
       print $client "$outcome\n";
    } else {
       print $client "refused\n";
    }
   #--------------------------- create password for new user (for auto-enrollment).
       } elsif ($userinput =~/^autocreatepassword:/) {
    if (isClient) {
       my ($cmd,$authparam,$cdom)=split(/:/,$userinput);
       my ($create_passwd,$authchk);
       ($authparam,$create_passwd,$authchk) = &localenroll::create_password($authparam,$cdom);
       print $client &escape($authparam.':'.$create_passwd.':'.$authchk)."\n";
    } else {
       print $client "refused\n";
    }
   #---------------------------  read and remove temporary files (for auto-enrollment).
       } elsif ($userinput =~/^autoretrieve:/) {
    if (isClient) {
       my ($cmd,$filename) = split(/:/,$userinput);
       my $source = $perlvar{'lonDaemons'}.'/tmp/'.$filename;
       if ( (-e $source) && ($filename ne '') ) {
    my $reply = '';
    if (open(my $fh,$source)) {
       while (<$fh>) {
    chomp($_);
    $_ =~ s/^\s+//g;
    $_ =~ s/\s+$//g;
    $reply .= $_;
       }
       close($fh);
       print $client &escape($reply)."\n";
   #                                unlink($source);
    } else {
       print $client "error\n";
    }
       } else {
    print $client "error\n";
       }
    } else {
       print $client "refused\n";
    }
   #---------------------  read and retrieve institutional code format (for support form).
       } elsif ($userinput =~/^autoinstcodeformat:/) {
    if (isClient) {
       my $reply;
       my($cmd,$cdom,$course) = split(/:/,$userinput);
       my @pairs = split/\&/,$course;
       my %instcodes = ();
       my %codes = ();
       my @codetitles = ();
       my %cat_titles = ();
       my %cat_order = ();
       foreach (@pairs) {
    my ($key,$value) = split/=/,$_;
    $instcodes{&unescape($key)} = &unescape($value);
       }
       my $formatreply = &localenroll::instcode_format($cdom,\%instcodes,\%codes,\@codetitles,\%cat_titles,\%cat_order);
       if ($formatreply eq 'ok') {
    my $codes_str = &hash2str(%codes);
    my $codetitles_str = &array2str(@codetitles);
    my $cat_titles_str = &hash2str(%cat_titles);
    my $cat_order_str = &hash2str(%cat_order);
    print $client $codes_str.':'.$codetitles_str.':'.$cat_titles_str.':'.$cat_order_str."\n";
       }
    } else {
       print $client "refused\n";
    }
   # ------------------------------------------------------------- unknown command
   
       } else {
    # unknown command
    print $client "unknown_cmd\n";
       }
   # -------------------------------------------------------------------- complete
       Debug("process_request - returning 1");
       return 1;
   }
   #
   #   Decipher encoded traffic
   #  Parameters:
   #     input      - Encoded data.
   #  Returns:
   #     Decoded data or undef if encryption key was not yet negotiated.
   #  Implicit input:
   #     cipher  - This global holds the negotiated encryption key.
   #
   sub decipher {
       my ($input)  = @_;
       my $output = '';
       
       
       if($cipher) {
    my($enc, $enclength, $encinput) = split(/:/, $input);
    for(my $encidx = 0; $encidx < length($encinput); $encidx += 16) {
       $output .= 
    $cipher->decrypt(pack("H16", substr($encinput, $encidx, 16)));
    }
    return substr($output, 0, $enclength);
       } else {
    return undef;
       }
   }
   
   #
   #   Register a command processor.  This function is invoked to register a sub
   #   to process a request.  Once registered, the ProcessRequest sub can automatically
   #   dispatch requests to an appropriate sub, and do the top level validity checking
   #   as well:
   #    - Is the keyword recognized.
   #    - Is the proper client type attempting the request.
   #    - Is the request encrypted if it has to be.
   #   Parameters:
   #    $request_name         - Name of the request being registered.
   #                           This is the command request that will match
   #                           against the hash keywords to lookup the information
   #                           associated with the dispatch information.
   #    $procedure           - Reference to a sub to call to process the request.
   #                           All subs get called as follows:
   #                             Procedure($cmd, $tail, $replyfd, $key)
   #                             $cmd    - the actual keyword that invoked us.
   #                             $tail   - the tail of the request that invoked us.
   #                             $replyfd- File descriptor connected to the client
   #    $must_encode          - True if the request must be encoded to be good.
   #    $client_ok            - True if it's ok for a client to request this.
   #    $manager_ok           - True if it's ok for a manager to request this.
   # Side effects:
   #      - On success, the Dispatcher hash has an entry added for the key $RequestName
   #      - On failure, the program will die as it's a bad internal bug to try to 
   #        register a duplicate command handler.
   #
   sub register_handler {
       my ($request_name,$procedure,$must_encode, $client_ok,$manager_ok)   = @_;
   
       #  Don't allow duplication#
      
       if (defined $Dispatcher{$request_name}) {
    die "Attempting to define a duplicate request handler for $request_name\n";
       }
       #   Build the client type mask:
       
       my $client_type_mask = 0;
       if($client_ok) {
    $client_type_mask  |= $CLIENT_OK;
       }
       if($manager_ok) {
    $client_type_mask  |= $MANAGER_OK;
       }
      
       #  Enter the hash:
         
       my @entry = ($procedure, $must_encode, $client_type_mask);
      
       $Dispatcher{$request_name} = \@entry;
      
      
   }
   
   
   #------------------------------------------------------------------
   
   
   
   
 #  #
 #  Convert an error return code from lcpasswd to a string value.  #  Convert an error return code from lcpasswd to a string value.
Line 507  sub catchexception { Line 3036  sub catchexception {
     $SIG{'QUIT'}='DEFAULT';      $SIG{'QUIT'}='DEFAULT';
     $SIG{__DIE__}='DEFAULT';      $SIG{__DIE__}='DEFAULT';
     &status("Catching exception");      &status("Catching exception");
     &logthis("<font color=red>CRITICAL: "      &logthis("<font color='red'>CRITICAL: "
      ."ABNORMAL EXIT. Child $$ for server $thisserver died through "       ."ABNORMAL EXIT. Child $$ for server $thisserver died through "
      ."a crash with this error msg->[$error]</font>");       ."a crash with this error msg->[$error]</font>");
     &logthis('Famous last words: '.$status.' - '.$lastlog);      &logthis('Famous last words: '.$status.' - '.$lastlog);
Line 518  sub catchexception { Line 3047  sub catchexception {
   
 sub timeout {  sub timeout {
     &status("Handling Timeout");      &status("Handling Timeout");
     &logthis("<font color=ref>CRITICAL: TIME OUT ".$$."</font>");      &logthis("<font color='red'>CRITICAL: TIME OUT ".$$."</font>");
     &catchexception('Timeout');      &catchexception('Timeout');
 }  }
 # -------------------------------- Set signal handlers to record abnormal exits  # -------------------------------- Set signal handlers to record abnormal exits
Line 570  $server = IO::Socket::INET->new(LocalPor Line 3099  $server = IO::Socket::INET->new(LocalPor
 # global variables  # global variables
   
 my %children               = ();       # keys are current child process IDs  my %children               = ();       # keys are current child process IDs
 my $children               = 0;        # current number of children  
   
 sub REAPER {                        # takes care of dead children  sub REAPER {                        # takes care of dead children
     $SIG{CHLD} = \&REAPER;      $SIG{CHLD} = \&REAPER;
     &status("Handling child death");      &status("Handling child death");
     my $pid = wait;      my $pid;
     if (defined($children{$pid})) {      do {
  &logthis("Child $pid died");   $pid = waitpid(-1,&WNOHANG());
  $children --;   if (defined($children{$pid})) {
  delete $children{$pid};      &logthis("Child $pid died");
     } else {      delete($children{$pid});
  &logthis("Unknown Child $pid died");   } elsif ($pid > 0) {
       &logthis("Unknown Child $pid died");
    }
       } while ( $pid > 0 );
       foreach my $child (keys(%children)) {
    $pid = waitpid($child,&WNOHANG());
    if ($pid > 0) {
       &logthis("Child $child - $pid looks like we missed it's death");
       delete($children{$pid});
    }
     }      }
     &status("Finished Handling child death");      &status("Finished Handling child death");
 }  }
Line 593  sub HUNTSMAN {                      # si Line 3130  sub HUNTSMAN {                      # si
     &logthis("Free socket: ".shutdown($server,2)); # free up socket      &logthis("Free socket: ".shutdown($server,2)); # free up socket
     my $execdir=$perlvar{'lonDaemons'};      my $execdir=$perlvar{'lonDaemons'};
     unlink("$execdir/logs/lond.pid");      unlink("$execdir/logs/lond.pid");
     &logthis("<font color=red>CRITICAL: Shutting down</font>");      &logthis("<font color='red'>CRITICAL: Shutting down</font>");
     &status("Done killing children");      &status("Done killing children");
     exit;                           # clean up with dignity      exit;                           # clean up with dignity
 }  }
Line 603  sub HUPSMAN {                      # sig Line 3140  sub HUPSMAN {                      # sig
     &status("Killing children for restart (HUP)");      &status("Killing children for restart (HUP)");
     kill 'INT' => keys %children;      kill 'INT' => keys %children;
     &logthis("Free socket: ".shutdown($server,2)); # free up socket      &logthis("Free socket: ".shutdown($server,2)); # free up socket
     &logthis("<font color=red>CRITICAL: Restarting</font>");      &logthis("<font color='red'>CRITICAL: Restarting</font>");
     my $execdir=$perlvar{'lonDaemons'};      my $execdir=$perlvar{'lonDaemons'};
     unlink("$execdir/logs/lond.pid");      unlink("$execdir/logs/lond.pid");
     &status("Restarting self (HUP)");      &status("Restarting self (HUP)");
Line 613  sub HUPSMAN {                      # sig Line 3150  sub HUPSMAN {                      # sig
 #  #
 #    Kill off hashes that describe the host table prior to re-reading it.  #    Kill off hashes that describe the host table prior to re-reading it.
 #    Hashes affected are:  #    Hashes affected are:
 #       %hostid, %hostdom %hostip  #       %hostid, %hostdom %hostip %hostdns.
 #  #
 sub KillHostHashes {  sub KillHostHashes {
     foreach my $key (keys %hostid) {      foreach my $key (keys %hostid) {
Line 625  sub KillHostHashes { Line 3162  sub KillHostHashes {
     foreach my $key (keys %hostip) {      foreach my $key (keys %hostip) {
  delete $hostip{$key};   delete $hostip{$key};
     }      }
       foreach my $key (keys %hostdns) {
    delete $hostdns{$key};
       }
 }  }
 #  #
 #   Read in the host table from file and distribute it into the various hashes:  #   Read in the host table from file and distribute it into the various hashes:
Line 635  sub KillHostHashes { Line 3175  sub KillHostHashes {
 sub ReadHostTable {  sub ReadHostTable {
   
     open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";      open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
           my $myloncapaname = $perlvar{'lonHostID'};
       Debug("My loncapa name is : $myloncapaname");
     while (my $configline=<CONFIG>) {      while (my $configline=<CONFIG>) {
  my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);   if (!($configline =~ /^\s*\#/)) {
  chomp($ip); $ip=~s/\D+$//;      my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
  $hostid{$ip}=$id;      chomp($ip); $ip=~s/\D+$//;
  $hostdom{$id}=$domain;      $hostid{$ip}=$id;         # LonCAPA name of host by IP.
  $hostip{$id}=$ip;      $hostdom{$id}=$domain;    # LonCAPA domain name of host. 
  if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; }      $hostip{$id}=$ip;      # IP address of host.
       $hostdns{$name} = $id;    # LonCAPA name of host by DNS.
   
       if ($id eq $perlvar{'lonHostID'}) { 
    Debug("Found me in the host table: $name");
    $thisserver=$name; 
       }
    }
     }      }
     close(CONFIG);      close(CONFIG);
 }  }
Line 704  sub checkchildren { Line 3252  sub checkchildren {
     &logthis('Going to check on the children');      &logthis('Going to check on the children');
     my $docdir=$perlvar{'lonDocRoot'};      my $docdir=$perlvar{'lonDocRoot'};
     foreach (sort keys %children) {      foreach (sort keys %children) {
  sleep 1;   #sleep 1;
         unless (kill 'USR1' => $_) {          unless (kill 'USR1' => $_) {
     &logthis ('Child '.$_.' is dead');      &logthis ('Child '.$_.' is dead');
             &logstatus($$.' is dead');              &logstatus($$.' is dead');
       delete($children{$_});
         }           } 
     }      }
     sleep 5;      sleep 5;
     $SIG{ALRM} = sub { die "timeout" };      $SIG{ALRM} = sub { Debug("timeout"); 
          die "timeout";  };
     $SIG{__DIE__} = 'DEFAULT';      $SIG{__DIE__} = 'DEFAULT';
     &status("Checking on the children (waiting for reports)");      &status("Checking on the children (waiting for reports)");
     foreach (sort keys %children) {      foreach (sort keys %children) {
Line 725  sub checkchildren { Line 3275  sub checkchildren {
     #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`;      #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`;
     #$execdir=$perlvar{'lonDaemons'};      #$execdir=$perlvar{'lonDaemons'};
     #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`;      #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`;
       delete($children{$_});
     alarm(0);      alarm(0);
   }    }
         }          }
Line 732  sub checkchildren { Line 3283  sub checkchildren {
     $SIG{ALRM} = 'DEFAULT';      $SIG{ALRM} = 'DEFAULT';
     $SIG{__DIE__} = \&catchexception;      $SIG{__DIE__} = \&catchexception;
     &status("Finished checking children");      &status("Finished checking children");
       &logthis('Finished Checking children');
 }  }
   
 # --------------------------------------------------------------------- Logging  # --------------------------------------------------------------------- Logging
Line 763  sub Debug { Line 3315  sub Debug {
 #     request - Original request from client.  #     request - Original request from client.
 #  #
 sub Reply {  sub Reply {
     my $fd      = shift;      my ($fd, $reply, $request) = @_;
     my $reply   = shift;  
     my $request = shift;  
   
     print $fd $reply;      print $fd $reply;
     Debug("Request was $request  Reply was $reply");      Debug("Request was $request  Reply was $reply");
   
       $Transactions++;
   
   
   }
   
   
   #
   #    Sub to report a failure.
   #    This function:
   #     -   Increments the failure statistic counters.
   #     -   Invokes Reply to send the error message to the client.
   # Parameters:
   #    fd       - File descriptor open on the client
   #    reply    - Reply text to emit.
   #    request  - The original request message (used by Reply
   #               to debug if that's enabled.
   # Implicit outputs:
   #    $Failures- The number of failures is incremented.
   #    Reply (invoked here) sends a message to the 
   #    client:
   #
   sub Failure {
       my $fd      = shift;
       my $reply   = shift;
       my $request = shift;
      
       $Failures++;
       Reply($fd, $reply, $request);      # That's simple eh?
 }  }
 # ------------------------------------------------------------------ Log status  # ------------------------------------------------------------------ Log status
   
Line 777  sub logstatus { Line 3354  sub logstatus {
     &status("Doing logging");      &status("Doing logging");
     my $docdir=$perlvar{'lonDocRoot'};      my $docdir=$perlvar{'lonDocRoot'};
     {      {
     my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt");  
     print $fh $$."\t".$currenthostid."\t".$status."\t".$lastlog."\n";  
     $fh->close();  
     }  
     &status("Finished londstatus.txt");  
     {  
  my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt");   my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt");
         print $fh $status."\n".$lastlog."\n".time;          print $fh $status."\n".$lastlog."\n".time."\n$keymode";
         $fh->close();          $fh->close();
     }      }
       &status("Finished $$.txt");
       {
    open(LOG,">>$docdir/lon-status/londstatus.txt");
    flock(LOG,LOCK_EX);
    print LOG $$."\t".$clientname."\t".$currenthostid."\t"
       .$status."\t".$lastlog."\t $keymode\n";
    flock(DB,LOCK_UN);
    close(LOG);
       }
     &status("Finished logging");      &status("Finished logging");
 }  }
   
Line 843  sub reconlonc { Line 3423  sub reconlonc {
             kill USR1 => $loncpid;              kill USR1 => $loncpid;
         } else {          } else {
     &logthis(      &logthis(
               "<font color=red>CRITICAL: "                "<font color='red'>CRITICAL: "
              ."lonc at pid $loncpid not responding, giving up</font>");               ."lonc at pid $loncpid not responding, giving up</font>");
         }          }
     } else {      } else {
       &logthis('<font color=red>CRITICAL: lonc not running, giving up</font>');        &logthis('<font color="red">CRITICAL: lonc not running, giving up</font>');
     }      }
 }  }
   
Line 951  my $execdir=$perlvar{'lonDaemons'}; Line 3531  my $execdir=$perlvar{'lonDaemons'};
 open (PIDSAVE,">$execdir/logs/lond.pid");  open (PIDSAVE,">$execdir/logs/lond.pid");
 print PIDSAVE "$$\n";  print PIDSAVE "$$\n";
 close(PIDSAVE);  close(PIDSAVE);
 &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");  &logthis("<font color='red'>CRITICAL: ---------- Starting ----------</font>");
 &status('Starting');  &status('Starting');
   
   
Line 984  while (1) { Line 3564  while (1) {
   
 sub make_new_child {  sub make_new_child {
     my $pid;      my $pid;
     my $cipher;  #    my $cipher;     # Now global
     my $sigset;      my $sigset;
   
     $client = shift;      $client = shift;
Line 1006  sub make_new_child { Line 3586  sub make_new_child {
     #  the pid hash.      #  the pid hash.
     #      #
     my $caller = getpeername($client);      my $caller = getpeername($client);
     my ($port,$iaddr)=unpack_sockaddr_in($caller);      my ($port,$iaddr);
     $clientip=inet_ntoa($iaddr);      if (defined($caller) && length($caller) > 0) {
    ($port,$iaddr)=unpack_sockaddr_in($caller);
       } else {
    &logthis("Unable to determine who caller was, getpeername returned nothing");
       }
       if (defined($iaddr)) {
    $clientip  = inet_ntoa($iaddr);
    Debug("Connected with $clientip");
    $clientdns = gethostbyaddr($iaddr, AF_INET);
    Debug("Connected with $clientdns by name");
       } else {
    &logthis("Unable to determine clientip");
    $clientip='Unavailable';
       }
           
     if ($pid) {      if ($pid) {
         # Parent records the child's birth and returns.          # Parent records the child's birth and returns.
         sigprocmask(SIG_UNBLOCK, $sigset)          sigprocmask(SIG_UNBLOCK, $sigset)
             or die "Can't unblock SIGINT for fork: $!\n";              or die "Can't unblock SIGINT for fork: $!\n";
         $children{$pid} = $clientip;          $children{$pid} = $clientip;
         $children++;  
         &status('Started child '.$pid);          &status('Started child '.$pid);
         return;          return;
     } else {      } else {
Line 1031  sub make_new_child { Line 3623  sub make_new_child {
         sigprocmask(SIG_UNBLOCK, $sigset)          sigprocmask(SIG_UNBLOCK, $sigset)
             or die "Can't unblock SIGINT for fork: $!\n";              or die "Can't unblock SIGINT for fork: $!\n";
   
         my $tmpsnum=0;  #        my $tmpsnum=0;            # Now global
 #---------------------------------------------------- kerberos 5 initialization  #---------------------------------------------------- kerberos 5 initialization
         &Authen::Krb5::init_context();          &Authen::Krb5::init_context();
         &Authen::Krb5::init_ets();          &Authen::Krb5::init_ets();
Line 1040  sub make_new_child { Line 3632  sub make_new_child {
 # =============================================================================  # =============================================================================
             # do something with the connection              # do something with the connection
 # -----------------------------------------------------------------------------  # -----------------------------------------------------------------------------
  # see if we know client and check for spoof IP by challenge   # see if we know client and 'check' for spoof IP by ineffective challenge
   
  ReadManagerTable; # May also be a manager!!   ReadManagerTable; # May also be a manager!!
   
Line 1058  sub make_new_child { Line 3650  sub make_new_child {
     $clientname = $managers{$clientip};      $clientname = $managers{$clientip};
  }   }
  my $clientok;   my $clientok;
   
  if ($clientrec || $ismanager) {   if ($clientrec || $ismanager) {
     &status("Waiting for init from $clientip $clientname");      &status("Waiting for init from $clientip $clientname");
     &logthis('<font color="yellow">INFO: Connection, '.      &logthis('<font color="yellow">INFO: Connection, '.
Line 1065  sub make_new_child { Line 3658  sub make_new_child {
   " ($clientname) connection type = $ConnectionType </font>" );    " ($clientname) connection type = $ConnectionType </font>" );
     &status("Connecting $clientip  ($clientname))");       &status("Connecting $clientip  ($clientname))"); 
     my $remotereq=<$client>;      my $remotereq=<$client>;
     $remotereq=~s/[^\w:]//g;      chomp($remotereq);
       Debug("Got init: $remotereq");
       my $inikeyword = split(/:/, $remotereq);
     if ($remotereq =~ /^init/) {      if ($remotereq =~ /^init/) {
  &sethost("sethost:$perlvar{'lonHostID'}");   &sethost("sethost:$perlvar{'lonHostID'}");
  my $challenge="$$".time;   #
  print $client "$challenge\n";   #  If the remote is attempting a local init... give that a try:
  &status(   #
  "Waiting for challenge reply from $clientip ($clientname)");    my ($i, $inittype) = split(/:/, $remotereq);
  $remotereq=<$client>;  
  $remotereq=~s/\W//g;   # If the connection type is ssl, but I didn't get my
  if ($challenge eq $remotereq) {   # certificate files yet, then I'll drop  back to 
     $clientok=1;   # insecure (if allowed).
     print $client "ok\n";  
    if($inittype eq "ssl") {
       my ($ca, $cert) = lonssl::CertificateFile;
       my $kfile       = lonssl::KeyFile;
       if((!$ca)   || 
          (!$cert) || 
          (!$kfile)) {
    $inittype = ""; # This forces insecure attempt.
    &logthis("<font color=\"blue\"> Certificates not "
    ."installed -- trying insecure auth</font>");
       }
       else { # SSL certificates are in place so
       } # Leave the inittype alone.
    }
   
    if($inittype eq "local") {
       my $key = LocalConnection($client, $remotereq);
       if($key) {
    Debug("Got local key $key");
    $clientok     = 1;
    my $cipherkey = pack("H32", $key);
    $cipher       = new IDEA($cipherkey);
    print $client "ok:local\n";
    &logthis('<font color="green"'
    . "Successful local authentication </font>");
    $keymode = "local"
       } else {
    Debug("Failed to get local key");
    $clientok = 0;
    shutdown($client, 3);
    close $client;
       }
    } elsif ($inittype eq "ssl") {
       my $key = SSLConnection($client);
       if ($key) {
    $clientok = 1;
    my $cipherkey = pack("H32", $key);
    $cipher       = new IDEA($cipherkey);
    &logthis('<font color="green">'
    ."Successfull ssl authentication with $clientname </font>");
    $keymode = "ssl";
        
       } else {
    $clientok = 0;
    close $client;
       }
      
  } else {   } else {
     &logthis(      my $ok = InsecureConnection($client);
      "<font color=blue>WARNING: $clientip did not reply challenge</font>");      if($ok) {
     &status('No challenge reply '.$clientip);   $clientok = 1;
    &logthis('<font color="green">'
    ."Successful insecure authentication with $clientname </font>");
    print $client "ok\n";
    $keymode = "insecure";
       } else {
    &logthis('<font color="yellow">'
     ."Attempted insecure connection disallowed </font>");
    close $client;
    $clientok = 0;
   
       }
  }   }
     } else {      } else {
  &logthis(   &logthis(
  "<font color=blue>WARNING: "   "<font color='blue'>WARNING: "
  ."$clientip failed to initialize: >$remotereq< </font>");   ."$clientip failed to initialize: >$remotereq< </font>");
  &status('No init '.$clientip);   &status('No init '.$clientip);
     }      }
       
  } else {   } else {
     &logthis(      &logthis(
      "<font color=blue>WARNING: Unknown client $clientip</font>");       "<font color='blue'>WARNING: Unknown client $clientip</font>");
     &status('Hung up on '.$clientip);      &status('Hung up on '.$clientip);
  }   }
    
  if ($clientok) {   if ($clientok) {
 # ---------------- New known client connecting, could mean machine online again  # ---------------- New known client connecting, could mean machine online again
           
Line 1104  sub make_new_child { Line 3758  sub make_new_child {
  }   }
  &reconlonc("$perlvar{'lonSockDir'}/$id");   &reconlonc("$perlvar{'lonSockDir'}/$id");
     }      }
     &logthis("<font color=green>Established connection: $clientname</font>");      &logthis("<font color='green'>Established connection: $clientname</font>");
     &status('Will listen to '.$clientname);      &status('Will listen to '.$clientname);
 # ------------------------------------------------------------ Process requests  # ------------------------------------------------------------ Process requests
     while (my $userinput=<$client>) {      my $keep_going = 1;
                 chomp($userinput);      my $user_input;
  Debug("Request = $userinput\n");      while(($user_input = get_request) && $keep_going) {
                 &status('Processing '.$clientname.': '.$userinput);   alarm(120);
                 my $wasenc=0;   Debug("Main: Got $user_input\n");
                 alarm(120);   $keep_going = &process_request($user_input);
 # ------------------------------------------------------------ See if encrypted  
  if ($userinput =~ /^enc/) {  
     if ($cipher) {  
  my ($cmd,$cmdlength,$encinput)=split(/:/,$userinput);  
  $userinput='';  
  for (my $encidx=0;$encidx<length($encinput);$encidx+=16) {  
     $userinput.=  
  $cipher->decrypt(  
  pack("H16",substr($encinput,$encidx,16))  
  );  
  }  
  $userinput=substr($userinput,0,$cmdlength);  
  $wasenc=1;  
     }  
  }  
   
 # ------------------------------------------------------------- Normal commands  
 # ------------------------------------------------------------------------ ping  
  if ($userinput =~ /^ping/) { # client only  
     if(isClient) {  
  print $client "$currenthostid\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
     }  
 # ------------------------------------------------------------------------ pong  
  }elsif ($userinput =~ /^pong/) { # client only  
     if(isClient) {  
  my $reply=&reply("ping",$clientname);  
  print $client "$currenthostid:$reply\n";   
     } else {  
  Reply($client, "refused\n", $userinput);  
     }  
 # ------------------------------------------------------------------------ ekey  
  } elsif ($userinput =~ /^ekey/) { # ok for both clients & mgrs  
     my $buildkey=time.$$.int(rand 100000);  
     $buildkey=~tr/1-6/A-F/;  
     $buildkey=int(rand 100000).$buildkey.int(rand 100000);  
     my $key=$currenthostid.$clientname;  
     $key=~tr/a-z/A-Z/;  
     $key=~tr/G-P/0-9/;  
     $key=~tr/Q-Z/0-9/;  
     $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;  
     $key=substr($key,0,32);  
     my $cipherkey=pack("H32",$key);  
     $cipher=new IDEA $cipherkey;  
     print $client "$buildkey\n";   
 # ------------------------------------------------------------------------ load  
  } elsif ($userinput =~ /^load/) { # client only  
     if (isClient) {  
  my $loadavg;  
  {  
     my $loadfile=IO::File->new('/proc/loadavg');  
     $loadavg=<$loadfile>;  
  }  
  $loadavg =~ s/\s.*//g;  
  my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};  
  print $client "$loadpercent\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # -------------------------------------------------------------------- userload  
  } elsif ($userinput =~ /^userload/) { # client only  
     if(isClient) {  
  my $userloadpercent=&userload();  
  print $client "$userloadpercent\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
        
     }  
 #  
 #        Transactions requiring encryption:  
 #  
 # ----------------------------------------------------------------- currentauth  
  } elsif ($userinput =~ /^currentauth/) {  
     if (($wasenc==1)  && isClient) { # Encoded & client only.  
  my ($cmd,$udom,$uname)=split(/:/,$userinput);  
  my $result = GetAuthType($udom, $uname);  
  if($result eq "nouser") {  
     print $client "unknown_user\n";  
  }  
  else {  
     print $client "$result\n"  
     }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 #--------------------------------------------------------------------- pushfile  
  } elsif($userinput =~ /^pushfile/) { # encoded & manager.  
     if(($wasenc == 1) && isManager) {  
  my $cert = GetCertificate($userinput);  
  if(ValidManager($cert)) {  
     my $reply = PushFile($userinput);  
     print $client "$reply\n";  
  } else {  
     print $client "refused\n";  
  }   
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 #--------------------------------------------------------------------- reinit  
  } elsif($userinput =~ /^reinit/) { # Encoded and manager  
  if (($wasenc == 1) && isManager) {  
  my $cert = GetCertificate($userinput);  
  if(ValidManager($cert)) {  
  chomp($userinput);  
  my $reply = ReinitProcess($userinput);  
  print $client  "$reply\n";  
  } else {  
  print $client "refused\n";  
  }  
  } else {  
  Reply($client, "refused\n", $userinput);  
  }  
 #------------------------------------------------------------------------- edit  
     } elsif ($userinput =~ /^edit/) {    # encoded and manager:  
  if(($wasenc ==1) && (isManager)) {  
     my $cert = GetCertificate($userinput);  
     if(ValidManager($cert)) {  
                my($command, $filetype, $script) = split(/:/, $userinput);  
                if (($filetype eq "hosts") || ($filetype eq "domain")) {  
                   if($script ne "") {  
                      Reply($client,"ok\n",$userinput);       # DEBUG: Call EditFile here.  
                   } else {  
                      Reply($client,"refused\n",$userinput);  
                   }  
                } else {  
                   Reply($client,"refused\n",$userinput);  
                }  
             } else {  
                Reply($client,"refused\n",$userinput);  
             }  
          } else {  
      Reply($client,"refused\n",$userinput);  
  }  
 # ------------------------------------------------------------------------ auth  
     } elsif ($userinput =~ /^auth/) { # Encoded and client only.  
     if (($wasenc==1) && isClient) {  
  my ($cmd,$udom,$uname,$upass)=split(/:/,$userinput);  
  chomp($upass);  
  $upass=unescape($upass);  
  my $proname=propath($udom,$uname);  
  my $passfilename="$proname/passwd";  
  if (-e $passfilename) {  
     my $pf = IO::File->new($passfilename);  
     my $realpasswd=<$pf>;  
     chomp($realpasswd);  
     my ($howpwd,$contentpwd)=split(/:/,$realpasswd);  
     my $pwdcorrect=0;  
     if ($howpwd eq 'internal') {  
  &Debug("Internal auth");  
  $pwdcorrect=  
     (crypt($upass,$contentpwd) eq $contentpwd);  
     } elsif ($howpwd eq 'unix') {  
  &Debug("Unix auth");  
  if((getpwnam($uname))[1] eq "") { #no such user!  
     $pwdcorrect = 0;  
  } else {  
     $contentpwd=(getpwnam($uname))[1];  
     my $pwauth_path="/usr/local/sbin/pwauth";  
     unless ($contentpwd eq 'x') {  
  $pwdcorrect=  
     (crypt($upass,$contentpwd) eq   
      $contentpwd);  
     }  
       
     elsif (-e $pwauth_path) {  
  open PWAUTH, "|$pwauth_path" or  
     die "Cannot invoke authentication";  
  print PWAUTH "$uname\n$upass\n";  
  close PWAUTH;  
  $pwdcorrect=!$?;  
     }  
  }  
     } elsif ($howpwd eq 'krb4') {  
  my $null=pack("C",0);  
  unless ($upass=~/$null/) {  
     my $krb4_error = &Authen::Krb4::get_pw_in_tkt  
  ($uname,"",$contentpwd,'krbtgt',  
  $contentpwd,1,$upass);  
     if (!$krb4_error) {  
  $pwdcorrect = 1;  
     } else {   
  $pwdcorrect=0;   
  # log error if it is not a bad password  
  if ($krb4_error != 62) {  
     &logthis('krb4:'.$uname.','.$contentpwd.','.  
      &Authen::Krb4::get_err_txt($Authen::Krb4::error));  
  }  
     }  
  }  
     } elsif ($howpwd eq 'krb5') {  
  my $null=pack("C",0);  
  unless ($upass=~/$null/) {  
     my $krbclient=&Authen::Krb5::parse_name($uname.'@'.$contentpwd);  
     my $krbservice="krbtgt/".$contentpwd."\@".$contentpwd;  
     my $krbserver=&Authen::Krb5::parse_name($krbservice);  
     my $credentials=&Authen::Krb5::cc_default();  
     $credentials->initialize($krbclient);  
     my $krbreturn =   
  &Authen::Krb5::get_in_tkt_with_password(  
  $krbclient,$krbserver,$upass,$credentials);  
 #  unless ($krbreturn) {  
 #      &logthis("Krb5 Error: ".  
 #       &Authen::Krb5::error());  
 #  }  
     $pwdcorrect = ($krbreturn == 1);  
  } else { $pwdcorrect=0; }  
     } elsif ($howpwd eq 'localauth') {  
  $pwdcorrect=&localauth::localauth($uname,$upass,  
   $contentpwd);  
     }  
     if ($pwdcorrect) {  
  print $client "authorized\n";  
     } else {  
  print $client "non_authorized\n";  
     }    
  } else {  
     print $client "unknown_user\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ---------------------------------------------------------------------- passwd  
  } elsif ($userinput =~ /^passwd/) { # encoded and client  
     if (($wasenc==1) && isClient) {  
  my   
     ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);  
  chomp($npass);  
  $upass=&unescape($upass);  
  $npass=&unescape($npass);  
  &Debug("Trying to change password for $uname");  
  my $proname=propath($udom,$uname);  
  my $passfilename="$proname/passwd";  
  if (-e $passfilename) {  
     my $realpasswd;  
     { my $pf = IO::File->new($passfilename);  
       $realpasswd=<$pf>; }  
     chomp($realpasswd);  
     my ($howpwd,$contentpwd)=split(/:/,$realpasswd);  
     if ($howpwd eq 'internal') {  
  &Debug("internal auth");  
  if (crypt($upass,$contentpwd) eq $contentpwd) {  
     my $salt=time;  
     $salt=substr($salt,6,2);  
     my $ncpass=crypt($npass,$salt);  
     {  
  my $pf;  
  if ($pf = IO::File->new(">$passfilename")) {  
     print $pf "internal:$ncpass\n";  
     &logthis("Result of password change for $uname: pwchange_success");  
     print $client "ok\n";  
  } else {  
     &logthis("Unable to open $uname passwd to change password");  
     print $client "non_authorized\n";  
  }  
     }               
       
  } else {  
     print $client "non_authorized\n";  
  }  
     } elsif ($howpwd eq 'unix') {  
  # Unix means we have to access /etc/password  
  # one way or another.  
  # First: Make sure the current password is  
  #        correct  
  &Debug("auth is unix");  
  $contentpwd=(getpwnam($uname))[1];  
  my $pwdcorrect = "0";  
  my $pwauth_path="/usr/local/sbin/pwauth";  
  unless ($contentpwd eq 'x') {  
     $pwdcorrect=  
  (crypt($upass,$contentpwd) eq $contentpwd);  
  } elsif (-e $pwauth_path) {  
     open PWAUTH, "|$pwauth_path" or  
  die "Cannot invoke authentication";  
     print PWAUTH "$uname\n$upass\n";  
     close PWAUTH;  
     &Debug("exited pwauth with $? ($uname,$upass) ");  
     $pwdcorrect=($? == 0);  
  }  
  if ($pwdcorrect) {  
     my $execdir=$perlvar{'lonDaemons'};  
     &Debug("Opening lcpasswd pipeline");  
     my $pf = IO::File->new("|$execdir/lcpasswd > $perlvar{'lonDaemons'}/logs/lcpasswd.log");  
     print $pf "$uname\n$npass\n$npass\n";  
     close $pf;  
     my $err = $?;  
     my $result = ($err>0 ? 'pwchange_failure'   
   : 'ok');  
     &logthis("Result of password change for $uname: ".  
      &lcpasswdstrerror($?));  
     print $client "$result\n";  
  } else {  
     print $client "non_authorized\n";  
  }  
     } else {  
  print $client "auth_mode_error\n";  
     }    
  } else {  
     print $client "unknown_user\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # -------------------------------------------------------------------- makeuser  
  } elsif ($userinput =~ /^makeuser/) { # encoded and client.  
     &Debug("Make user received");  
     my $oldumask=umask(0077);  
     if (($wasenc==1) && isClient) {  
  my   
     ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);  
  &Debug("cmd =".$cmd." $udom =".$udom.  
        " uname=".$uname);  
  chomp($npass);  
  $npass=&unescape($npass);  
  my $proname=propath($udom,$uname);  
  my $passfilename="$proname/passwd";  
  &Debug("Password file created will be:".  
        $passfilename);  
  if (-e $passfilename) {  
     print $client "already_exists\n";  
  } elsif ($udom ne $currentdomainid) {  
     print $client "not_right_domain\n";  
  } else {  
     my @fpparts=split(/\//,$proname);  
     my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];  
     my $fperror='';  
     for (my $i=3;$i<=$#fpparts;$i++) {  
  $fpnow.='/'.$fpparts[$i];   
  unless (-e $fpnow) {  
     unless (mkdir($fpnow,0777)) {  
  $fperror="error: ".($!+0)  
     ." mkdir failed while attempting "  
     ."makeuser\n";  
     }  
  }  
     }  
     unless ($fperror) {  
  my $result=&make_passwd_file($uname, $umode,$npass,  
      $passfilename);  
  print $client $result;  
     } else {  
  print $client "$fperror\n";  
     }  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
         
     }  
     umask($oldumask);  
 # -------------------------------------------------------------- changeuserauth  
  } elsif ($userinput =~ /^changeuserauth/) { # encoded & client  
     &Debug("Changing authorization");  
     if (($wasenc==1) && isClient) {  
  my   
     ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);  
  chomp($npass);  
  &Debug("cmd = ".$cmd." domain= ".$udom.  
        "uname =".$uname." umode= ".$umode);  
  $npass=&unescape($npass);  
  my $proname=&propath($udom,$uname);  
  my $passfilename="$proname/passwd";  
  if ($udom ne $currentdomainid) {  
     print $client "not_right_domain\n";  
  } else {  
     my $result=&make_passwd_file($uname, $umode,$npass,  
  $passfilename);  
     print $client $result;  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
      
     }  
 # ------------------------------------------------------------------------ home  
  } elsif ($userinput =~ /^home/) { # client clear or encoded  
     if(isClient) {  
  my ($cmd,$udom,$uname)=split(/:/,$userinput);  
  chomp($uname);  
  my $proname=propath($udom,$uname);  
  if (-e $proname) {  
     print $client "found\n";  
  } else {  
     print $client "not_found\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ---------------------------------------------------------------------- update  
  } elsif ($userinput =~ /^update/) { # client clear or encoded.  
     if(isClient) {  
  my ($cmd,$fname)=split(/:/,$userinput);  
  my $ownership=ishome($fname);  
  if ($ownership eq 'not_owner') {  
     if (-e $fname) {  
  my ($dev,$ino,$mode,$nlink,  
     $uid,$gid,$rdev,$size,  
     $atime,$mtime,$ctime,  
     $blksize,$blocks)=stat($fname);  
  my $now=time;  
  my $since=$now-$atime;  
  if ($since>$perlvar{'lonExpire'}) {  
     my $reply=  
  &reply("unsub:$fname","$clientname");  
     unlink("$fname");  
  } else {  
     my $transname="$fname.in.transfer";  
     my $remoteurl=  
  &reply("sub:$fname","$clientname");  
     my $response;  
     {  
  my $ua=new LWP::UserAgent;  
  my $request=new HTTP::Request('GET',"$remoteurl");  
  $response=$ua->request($request,$transname);  
     }  
     if ($response->is_error()) {  
  unlink($transname);  
  my $message=$response->status_line;  
  &logthis(  
  "LWP GET: $message for $fname ($remoteurl)");  
     } else {  
  if ($remoteurl!~/\.meta$/) {  
     my $ua=new LWP::UserAgent;  
     my $mrequest=  
  new HTTP::Request('GET',$remoteurl.'.meta');  
     my $mresponse=  
  $ua->request($mrequest,$fname.'.meta');  
     if ($mresponse->is_error()) {  
  unlink($fname.'.meta');  
     }  
  }  
  rename($transname,$fname);  
     }  
  }  
  print $client "ok\n";  
     } else {  
  print $client "not_found\n";  
     }  
  } else {  
     print $client "rejected\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # -------------------------------------- fetch a user file from a remote server  
  } elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc.  
     if(isClient) {  
  my ($cmd,$fname)=split(/:/,$userinput);  
  my ($udom,$uname,$ufile)=split(/\//,$fname);  
  my $udir=propath($udom,$uname).'/userfiles';  
  unless (-e $udir) { mkdir($udir,0770); }  
  if (-e $udir) {  
     $ufile=~s/^[\.\~]+//;  
     $ufile=~s/\///g;  
     my $destname=$udir.'/'.$ufile;  
     my $transname=$udir.'/'.$ufile.'.in.transit';  
     my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;  
     my $response;  
     {  
  my $ua=new LWP::UserAgent;  
  my $request=new HTTP::Request('GET',"$remoteurl");  
  $response=$ua->request($request,$transname);  
     }  
     if ($response->is_error()) {  
  unlink($transname);  
  my $message=$response->status_line;  
  &logthis("LWP GET: $message for $fname ($remoteurl)");  
  print $client "failed\n";  
     } else {  
  if (!rename($transname,$destname)) {  
     &logthis("Unable to move $transname to $destname");  
     unlink($transname);  
     print $client "failed\n";  
  } else {  
     print $client "ok\n";  
  }  
     }  
  } else {  
     print $client "not_home\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------ authenticate access to a user file  
  } elsif ($userinput =~ /^tokenauthuserfile/) { # Client only  
     if(isClient) {  
  my ($cmd,$fname,$session)=split(/:/,$userinput);  
  chomp($session);  
  my $reply='non_auth';  
  if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.  
  $session.'.id')) {  
     while (my $line=<ENVIN>) {  
  if ($line=~/userfile\.$fname\=/) { $reply='ok'; }  
     }  
     close(ENVIN);  
     print $client $reply."\n";  
  } else {  
     print $client "invalid_token\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ----------------------------------------------------------------- unsubscribe  
  } elsif ($userinput =~ /^unsub/) {  
     if(isClient) {  
  my ($cmd,$fname)=split(/:/,$userinput);  
  if (-e $fname) {  
     print $client &unsub($client,$fname,$clientip);  
  } else {  
     print $client "not_found\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------------------------------- subscribe  
  } elsif ($userinput =~ /^sub/) {  
     if(isClient) {  
  print $client &subscribe($userinput,$clientip);  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------------------------- current version  
  } elsif ($userinput =~ /^currentversion/) {  
     if(isClient) {  
  my ($cmd,$fname)=split(/:/,$userinput);  
  print $client &currentversion($fname)."\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------------------------------------- log  
  } elsif ($userinput =~ /^log/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);  
  chomp($what);  
  my $proname=propath($udom,$uname);  
  my $now=time;  
  {  
     my $hfh;  
     if ($hfh=IO::File->new(">>$proname/activity.log")) {   
  print $hfh "$now:$clientname:$what\n";  
  print $client "ok\n";   
     } else {  
  print $client "error: ".($!+0)  
     ." IO::File->new Failed "  
     ."while attempting log\n";  
     }  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------------------------------------- put  
  } elsif ($userinput =~ /^put/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$what)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  if ($namespace ne 'roles') {  
     chomp($what);  
     my $proname=propath($udom,$uname);  
     my $now=time;  
     unless ($namespace=~/^nohist\_/) {  
  my $hfh;  
  if (  
     $hfh=IO::File->new(">>$proname/$namespace.hist")  
     ) { print $hfh "P:$now:$what\n"; }  
     }  
     my @pairs=split(/\&/,$what);  
     my %hash;  
     if (tie(%hash,'GDBM_File',  
     "$proname/$namespace.db",  
     &GDBM_WRCREAT(),0640)) {  
  foreach my $pair (@pairs) {  
     my ($key,$value)=split(/=/,$pair);  
     $hash{$key}=$value;  
  }  
  if (untie(%hash)) {  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)  
  ." untie(GDBM) failed ".  
  "while attempting put\n";  
  }  
     } else {  
  print $client "error: ".($!)  
     ." tie(GDBM) Failed ".  
     "while attempting put\n";  
     }  
  } else {  
     print $client "refused\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------------------------------- inc  
  } elsif ($userinput =~ /^inc:/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$what)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  if ($namespace ne 'roles') {  
     chomp($what);  
     my $proname=propath($udom,$uname);  
     my $now=time;  
     unless ($namespace=~/^nohist\_/) {  
  my $hfh;  
  if (  
     $hfh=IO::File->new(">>$proname/$namespace.hist")  
     ) { print $hfh "P:$now:$what\n"; }  
     }  
     my @pairs=split(/\&/,$what);  
     my %hash;  
     if (tie(%hash,'GDBM_File',  
     "$proname/$namespace.db",  
     &GDBM_WRCREAT(),0640)) {  
  foreach my $pair (@pairs) {  
     my ($key,$value)=split(/=/,$pair);  
                                     # We could check that we have a number...  
                                     if (! defined($value) || $value eq '') {  
                                         $value = 1;  
                                     }  
     $hash{$key}+=$value;  
  }  
  if (untie(%hash)) {  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)  
  ." untie(GDBM) failed ".  
  "while attempting put\n";  
  }  
     } else {  
  print $client "error: ".($!)  
     ." tie(GDBM) Failed ".  
     "while attempting put\n";  
     }  
  } else {  
     print $client "refused\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # -------------------------------------------------------------------- rolesput  
  } elsif ($userinput =~ /^rolesput/) {  
     if(isClient) {  
  &Debug("rolesput");  
  if ($wasenc==1) {  
     my ($cmd,$exedom,$exeuser,$udom,$uname,$what)  
  =split(/:/,$userinput);  
     &Debug("cmd = ".$cmd." exedom= ".$exedom.  
    "user = ".$exeuser." udom=".$udom.  
    "what = ".$what);  
     my $namespace='roles';  
     chomp($what);  
     my $proname=propath($udom,$uname);  
     my $now=time;  
     {  
  my $hfh;  
  if (  
     $hfh=IO::File->new(">>$proname/$namespace.hist")  
     ) {   
     print $hfh "P:$now:$exedom:$exeuser:$what\n";  
  }  
     }  
     my @pairs=split(/\&/,$what);  
     my %hash;  
     if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {  
  foreach my $pair (@pairs) {  
     my ($key,$value)=split(/=/,$pair);  
     &ManagePermissions($key, $udom, $uname,  
        &GetAuthType( $udom,   
      $uname));  
     $hash{$key}=$value;  
  }  
  if (untie(%hash)) {  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)  
  ." untie(GDBM) Failed ".  
  "while attempting rolesput\n";  
  }  
     } else {  
  print $client "error: ".($!+0)  
     ." tie(GDBM) Failed ".  
     "while attempting rolesput\n";  
     }  
  } else {  
     print $client "refused\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
     
     }  
 # -------------------------------------------------------------------- rolesdel  
  } elsif ($userinput =~ /^rolesdel/) {  
     if(isClient) {  
  &Debug("rolesdel");  
  if ($wasenc==1) {  
     my ($cmd,$exedom,$exeuser,$udom,$uname,$what)  
  =split(/:/,$userinput);  
     &Debug("cmd = ".$cmd." exedom= ".$exedom.  
    "user = ".$exeuser." udom=".$udom.  
    "what = ".$what);  
     my $namespace='roles';  
     chomp($what);  
     my $proname=propath($udom,$uname);  
     my $now=time;  
     {  
  my $hfh;  
  if (  
     $hfh=IO::File->new(">>$proname/$namespace.hist")  
     ) {   
     print $hfh "D:$now:$exedom:$exeuser:$what\n";  
  }  
     }  
     my @rolekeys=split(/\&/,$what);  
     my %hash;  
     if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {  
  foreach my $key (@rolekeys) {  
     delete $hash{$key};  
  }  
  if (untie(%hash)) {  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)  
  ." untie(GDBM) Failed ".  
  "while attempting rolesdel\n";  
  }  
     } else {  
  print $client "error: ".($!+0)  
     ." tie(GDBM) Failed ".  
     "while attempting rolesdel\n";  
     }  
  } else {  
     print $client "refused\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
         
     }  
 # ------------------------------------------------------------------------- get  
  } elsif ($userinput =~ /^get/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$what)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  chomp($what);  
  my @queries=split(/\&/,$what);  
  my $proname=propath($udom,$uname);  
  my $qresult='';  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {  
     for (my $i=0;$i<=$#queries;$i++) {  
  $qresult.="$hash{$queries[$i]}&";  
     }  
     if (untie(%hash)) {  
  $qresult=~s/\&$//;  
  print $client "$qresult\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting get\n";  
     }  
  } else {  
     if ($!+0 == 2) {  
  print $client "error:No such file or ".  
     "GDBM reported bad block error\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." tie(GDBM) Failed ".  
     "while attempting get\n";  
     }  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ------------------------------------------------------------------------ eget  
  } elsif ($userinput =~ /^eget/) {  
     if (isClient) {  
  my ($cmd,$udom,$uname,$namespace,$what)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  chomp($what);  
  my @queries=split(/\&/,$what);  
  my $proname=propath($udom,$uname);  
  my $qresult='';  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {  
     for (my $i=0;$i<=$#queries;$i++) {  
  $qresult.="$hash{$queries[$i]}&";  
     }  
     if (untie(%hash)) {  
  $qresult=~s/\&$//;  
  if ($cipher) {  
     my $cmdlength=length($qresult);  
     $qresult.="         ";  
     my $encqresult='';  
     for   
  (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {  
     $encqresult.=  
  unpack("H16",  
        $cipher->encrypt(substr($qresult,$encidx,8)));  
  }  
     print $client "enc:$cmdlength:$encqresult\n";  
  } else {  
     print $client "error:no_key\n";  
  }  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting eget\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting eget\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
       
     }  
 # ------------------------------------------------------------------------- del  
  } elsif ($userinput =~ /^del/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$what)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  chomp($what);  
  my $proname=propath($udom,$uname);  
  my $now=time;  
  unless ($namespace=~/^nohist\_/) {  
     my $hfh;  
     if (  
  $hfh=IO::File->new(">>$proname/$namespace.hist")  
  ) { print $hfh "D:$now:$what\n"; }  
  }  
  my @keys=split(/\&/,$what);  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {  
     foreach my $key (@keys) {  
  delete($hash{$key});  
     }  
     if (untie(%hash)) {  
  print $client "ok\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting del\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting del\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
   
     }  
 # ------------------------------------------------------------------------ keys  
  } elsif ($userinput =~ /^keys/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  my $proname=propath($udom,$uname);  
  my $qresult='';  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {  
     foreach my $key (keys %hash) {  
  $qresult.="$key&";  
     }  
     if (untie(%hash)) {  
  $qresult=~s/\&$//;  
  print $client "$qresult\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting keys\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting keys\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
      
     }  
 # ----------------------------------------------------------------- dumpcurrent  
  } elsif ($userinput =~ /^currentdump/) {  
     if (isClient) {  
  my ($cmd,$udom,$uname,$namespace)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  my $qresult='';  
  my $proname=propath($udom,$uname);  
  my %hash;  
  if (tie(%hash,'GDBM_File',  
  "$proname/$namespace.db",  
  &GDBM_READER(),0640)) {  
     # Structure of %data:  
     # $data{$symb}->{$parameter}=$value;  
     # $data{$symb}->{'v.'.$parameter}=$version;  
     # since $parameter will be unescaped, we do not  
     # have to worry about silly parameter names...  
     my %data = ();  
     while (my ($key,$value) = each(%hash)) {  
  my ($v,$symb,$param) = split(/:/,$key);  
  next if ($v eq 'version' || $symb eq 'keys');  
  next if (exists($data{$symb}) &&   
  exists($data{$symb}->{$param}) &&  
  $data{$symb}->{'v.'.$param} > $v);  
  $data{$symb}->{$param}=$value;  
  $data{$symb}->{'v.'.$param}=$v;  
     }  
     if (untie(%hash)) {  
  while (my ($symb,$param_hash) = each(%data)) {  
     while(my ($param,$value) = each (%$param_hash)){  
  next if ($param =~ /^v\./);  
  $qresult.=$symb.':'.$param.'='.$value.'&';  
     }  
  }  
  chop($qresult);  
  print $client "$qresult\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting currentdump\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting currentdump\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
     }  
 # ------------------------------------------------------------------------ dump  
  } elsif ($userinput =~ /^dump/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$regexp)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  if (defined($regexp)) {  
     $regexp=&unescape($regexp);  
  } else {  
     $regexp='.';  
  }  
  my $qresult='';  
  my $proname=propath($udom,$uname);  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {  
        study($regexp);  
        while (my ($key,$value) = each(%hash)) {  
    if ($regexp eq '.') {  
        $qresult.=$key.'='.$value.'&';  
    } else {  
        my $unescapeKey = &unescape($key);  
        if (eval('$unescapeKey=~/$regexp/')) {  
    $qresult.="$key=$value&";  
        }  
    }  
        }  
        if (untie(%hash)) {  
    chop($qresult);  
    print $client "$qresult\n";  
        } else {  
    print $client "error: ".($!+0)  
        ." untie(GDBM) Failed ".  
                                        "while attempting dump\n";  
        }  
    } else {  
        print $client "error: ".($!+0)  
    ." tie(GDBM) Failed ".  
    "while attempting dump\n";  
    }  
     } else {  
  Reply($client, "refused\n", $userinput);  
     
     }  
 # ----------------------------------------------------------------------- store  
  } elsif ($userinput =~ /^store/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$rid,$what)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  if ($namespace ne 'roles') {  
     chomp($what);  
     my $proname=propath($udom,$uname);  
     my $now=time;  
     unless ($namespace=~/^nohist\_/) {  
  my $hfh;  
  if (  
     $hfh=IO::File->new(">>$proname/$namespace.hist")  
     ) { print $hfh "P:$now:$rid:$what\n"; }  
     }  
     my @pairs=split(/\&/,$what);  
     my %hash;  
     if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {  
  my @previouskeys=split(/&/,$hash{"keys:$rid"});  
  my $key;  
  $hash{"version:$rid"}++;  
  my $version=$hash{"version:$rid"};  
  my $allkeys='';   
  foreach my $pair (@pairs) {  
     my ($key,$value)=split(/=/,$pair);  
     $allkeys.=$key.':';  
     $hash{"$version:$rid:$key"}=$value;  
  }  
  $hash{"$version:$rid:timestamp"}=$now;  
  $allkeys.='timestamp';  
  $hash{"$version:keys:$rid"}=$allkeys;  
  if (untie(%hash)) {  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)  
  ." untie(GDBM) Failed ".  
  "while attempting store\n";  
  }  
     } else {  
  print $client "error: ".($!+0)  
     ." tie(GDBM) Failed ".  
     "while attempting store\n";  
     }  
  } else {  
     print $client "refused\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
        
     }  
 # --------------------------------------------------------------------- restore  
  } elsif ($userinput =~ /^restore/) {  
     if(isClient) {  
  my ($cmd,$udom,$uname,$namespace,$rid)  
     =split(/:/,$userinput);  
  $namespace=~s/\//\_/g;  
  $namespace=~s/\W//g;  
  chomp($rid);  
  my $proname=propath($udom,$uname);  
  my $qresult='';  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {  
     my $version=$hash{"version:$rid"};  
     $qresult.="version=$version&";  
     my $scope;  
     for ($scope=1;$scope<=$version;$scope++) {  
  my $vkeys=$hash{"$scope:keys:$rid"};  
  my @keys=split(/:/,$vkeys);  
  my $key;  
  $qresult.="$scope:keys=$vkeys&";  
  foreach $key (@keys) {  
     $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";  
  }                                    
     }  
     if (untie(%hash)) {  
  $qresult=~s/\&$//;  
  print $client "$qresult\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting restore\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting restore\n";  
  }  
     } else  {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # -------------------------------------------------------------------- chatsend  
  } elsif ($userinput =~ /^chatsend/) {  
     if(isClient) {  
  my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput);  
  &chatadd($cdom,$cnum,$newpost);  
  print $client "ok\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
         
     }  
 # -------------------------------------------------------------------- chatretr  
  } elsif ($userinput =~ /^chatretr/) {  
     if(isClient) {  
  my   
     ($cmd,$cdom,$cnum,$udom,$uname)=split(/\:/,$userinput);  
  my $reply='';  
  foreach (&getchat($cdom,$cnum,$udom,$uname)) {  
     $reply.=&escape($_).':';  
  }  
  $reply=~s/\:$//;  
  print $client $reply."\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ------------------------------------------------------------------- querysend  
  } elsif ($userinput =~ /^querysend/) {  
     if(isClient) {  
  my ($cmd,$query,  
     $arg1,$arg2,$arg3)=split(/\:/,$userinput);  
  $query=~s/\n*$//g;  
  print $client "".  
     sqlreply("$clientname\&$query".  
      "\&$arg1"."\&$arg2"."\&$arg3")."\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
         
     }  
 # ------------------------------------------------------------------ queryreply  
  } elsif ($userinput =~ /^queryreply/) {  
     if(isClient) {  
  my ($cmd,$id,$reply)=split(/:/,$userinput);   
  my $store;  
  my $execdir=$perlvar{'lonDaemons'};  
  if ($store=IO::File->new(">$execdir/tmp/$id")) {  
     $reply=~s/\&/\n/g;  
     print $store $reply;  
     close $store;  
     my $store2=IO::File->new(">$execdir/tmp/$id.end");  
     print $store2 "done\n";  
     close $store2;  
     print $client "ok\n";  
  }  
  else {  
     print $client "error: ".($!+0)  
  ." IO::File->new Failed ".  
  "while attempting queryreply\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
        
     }  
 # ----------------------------------------------------------------- courseidput  
  } elsif ($userinput =~ /^courseidput/) {  
     if(isClient) {  
  my ($cmd,$udom,$what)=split(/:/,$userinput);  
  chomp($what);  
  $udom=~s/\W//g;  
  my $proname=  
     "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";  
  my $now=time;  
  my @pairs=split(/\&/,$what);  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {  
     foreach my $pair (@pairs) {  
  my ($key,$value)=split(/=/,$pair);  
  $hash{$key}=$value.':'.$now;  
     }  
     if (untie(%hash)) {  
  print $client "ok\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting courseidput\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting courseidput\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ---------------------------------------------------------------- courseiddump  
  } elsif ($userinput =~ /^courseiddump/) {  
     if(isClient) {  
  my ($cmd,$udom,$since,$description)  
     =split(/:/,$userinput);  
  if (defined($description)) {  
     $description=&unescape($description);  
  } else {  
     $description='.';  
  }  
  unless (defined($since)) { $since=0; }  
  my $qresult='';  
  my $proname=  
     "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {  
     while (my ($key,$value) = each(%hash)) {  
  my ($descr,$lasttime)=split(/\:/,$value);  
  if ($lasttime<$since) { next; }  
  if ($description eq '.') {  
     $qresult.=$key.'='.$descr.'&';  
  } else {  
     my $unescapeVal = &unescape($descr);  
     if (eval('$unescapeVal=~/$description/i')) {  
  $qresult.="$key=$descr&";  
     }  
  }  
     }  
     if (untie(%hash)) {  
  chop($qresult);  
  print $client "$qresult\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting courseiddump\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting courseiddump\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ----------------------------------------------------------------------- idput  
  } elsif ($userinput =~ /^idput/) {  
     if(isClient) {  
  my ($cmd,$udom,$what)=split(/:/,$userinput);  
  chomp($what);  
  $udom=~s/\W//g;  
  my $proname="$perlvar{'lonUsersDir'}/$udom/ids";  
  my $now=time;  
  {  
     my $hfh;  
     if (  
  $hfh=IO::File->new(">>$proname.hist")  
  ) { print $hfh "P:$now:$what\n"; }  
  }  
  my @pairs=split(/\&/,$what);  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {  
     foreach my $pair (@pairs) {  
  my ($key,$value)=split(/=/,$pair);  
  $hash{$key}=$value;  
     }  
     if (untie(%hash)) {  
  print $client "ok\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting idput\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting idput\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ----------------------------------------------------------------------- idget  
  } elsif ($userinput =~ /^idget/) {  
     if(isClient) {  
  my ($cmd,$udom,$what)=split(/:/,$userinput);  
  chomp($what);  
  $udom=~s/\W//g;  
  my $proname="$perlvar{'lonUsersDir'}/$udom/ids";  
  my @queries=split(/\&/,$what);  
  my $qresult='';  
  my %hash;  
  if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {  
     for (my $i=0;$i<=$#queries;$i++) {  
  $qresult.="$hash{$queries[$i]}&";  
     }  
     if (untie(%hash)) {  
  $qresult=~s/\&$//;  
  print $client "$qresult\n";  
     } else {  
  print $client "error: ".($!+0)  
     ." untie(GDBM) Failed ".  
     "while attempting idget\n";  
     }  
  } else {  
     print $client "error: ".($!+0)  
  ." tie(GDBM) Failed ".  
  "while attempting idget\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ---------------------------------------------------------------------- tmpput  
  } elsif ($userinput =~ /^tmpput/) {  
     if(isClient) {  
  my ($cmd,$what)=split(/:/,$userinput);  
  my $store;  
  $tmpsnum++;  
  my $id=$$.'_'.$clientip.'_'.$tmpsnum;  
  $id=~s/\W/\_/g;  
  $what=~s/\n//g;  
  my $execdir=$perlvar{'lonDaemons'};  
  if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {  
     print $store $what;  
     close $store;  
     print $client "$id\n";  
  }  
  else {  
     print $client "error: ".($!+0)  
  ."IO::File->new Failed ".  
  "while attempting tmpput\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
       
     }  
       
 # ---------------------------------------------------------------------- tmpget  
  } elsif ($userinput =~ /^tmpget/) {  
     if(isClient) {  
  my ($cmd,$id)=split(/:/,$userinput);  
  chomp($id);  
  $id=~s/\W/\_/g;  
  my $store;  
  my $execdir=$perlvar{'lonDaemons'};  
  if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {  
     my $reply=<$store>;  
     print $client "$reply\n";  
     close $store;  
  }  
  else {  
     print $client "error: ".($!+0)  
  ."IO::File->new Failed ".  
  "while attempting tmpget\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
         
     }  
 # ---------------------------------------------------------------------- tmpdel  
  } elsif ($userinput =~ /^tmpdel/) {  
     if(isClient) {  
  my ($cmd,$id)=split(/:/,$userinput);  
  chomp($id);  
  $id=~s/\W/\_/g;  
  my $execdir=$perlvar{'lonDaemons'};  
  if (unlink("$execdir/tmp/$id.tmp")) {  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)  
  ."Unlink tmp Failed ".  
  "while attempting tmpdel\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
        
     }  
 # -------------------------------------------------------------------------- ls  
  } elsif ($userinput =~ /^ls/) {  
     if(isClient) {  
  my ($cmd,$ulsdir)=split(/:/,$userinput);  
  my $ulsout='';  
  my $ulsfn;  
  if (-e $ulsdir) {  
     if(-d $ulsdir) {  
  if (opendir(LSDIR,$ulsdir)) {  
     while ($ulsfn=readdir(LSDIR)) {  
  my @ulsstats=stat($ulsdir.'/'.$ulsfn);  
  $ulsout.=$ulsfn.'&'.  
     join('&',@ulsstats).':';  
     }  
     closedir(LSDIR);  
  }  
     } else {  
  my @ulsstats=stat($ulsdir);  
  $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';  
     }  
  } else {  
     $ulsout='no_such_dir';  
  }  
  if ($ulsout eq '') { $ulsout='empty'; }  
  print $client "$ulsout\n";  
     } else {  
  Reply($client, "refused\n", $userinput);  
        
     }  
 # ----------------------------------------------------------------- setannounce  
  } elsif ($userinput =~ /^setannounce/) {  
     if (isClient) {  
  my ($cmd,$announcement)=split(/:/,$userinput);  
  chomp($announcement);  
  $announcement=&unescape($announcement);  
  if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}.  
     '/announcement.txt')) {  
     print $store $announcement;  
     close $store;  
     print $client "ok\n";  
  } else {  
     print $client "error: ".($!+0)."\n";  
  }  
     } else {  
  Reply($client, "refused\n", $userinput);  
          
     }  
 # ------------------------------------------------------------------ Hanging up  
  } elsif (($userinput =~ /^exit/) ||  
  ($userinput =~ /^init/)) { # no restrictions.  
     &logthis(  
      "Client $clientip ($clientname) hanging up: $userinput");  
     print $client "bye\n";  
     $client->shutdown(2);        # shutdown the socket forcibly.  
     $client->close();  
     last;  
   
 # ---------------------------------- set current host/domain  
  } elsif ($userinput =~ /^sethost:/) {  
     if (isClient) {  
  print $client &sethost($userinput)."\n";  
     } else {  
  print $client "refused\n";  
     }  
 #---------------------------------- request file (?) version.  
  } elsif ($userinput =~/^version:/) {  
     if (isClient) {  
  print $client &version($userinput)."\n";  
     } else {  
  print $client "refused\n";  
     }  
 # ------------------------------------------------------------- unknown command  
   
  } else {  
     # unknown command  
     print $client "unknown_cmd\n";  
  }  
 # -------------------------------------------------------------------- complete  
  alarm(0);   alarm(0);
  &status('Listening to '.$clientname);   &status('Listening to '.$clientname." ($keymode)");   
     }      }
   
 # --------------------------------------------- client unknown or fishy, refuse  # --------------------------------------------- client unknown or fishy, refuse
  } else {   }  else {
     print $client "refused\n";      print $client "refused\n";
     $client->close();      $client->close();
     &logthis("<font color=blue>WARNING: "      &logthis("<font color='blue'>WARNING: "
      ."Rejected client $clientip, closing connection</font>");       ."Rejected client $clientip, closing connection</font>");
  }   }
     }                   }            
           
 # =============================================================================  # =============================================================================
           
     &logthis("<font color=red>CRITICAL: "      &logthis("<font color='red'>CRITICAL: "
      ."Disconnect from $clientip ($clientname)</font>");           ."Disconnect from $clientip ($clientname)</font>");    
           
           
Line 2596  sub make_new_child { Line 3807  sub make_new_child {
 #  #
 sub ManagePermissions  sub ManagePermissions
 {  {
     my $request = shift;  
     my $domain  = shift;      my ($request, $domain, $user, $authtype) = @_;
     my $user    = shift;  
     my $authtype= shift;  
   
     # See if the request is of the form /$domain/_au      # See if the request is of the form /$domain/_au
     &logthis("ruequest is $request");  
     if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput...      if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput...
  my $execdir = $perlvar{'lonDaemons'};   my $execdir = $perlvar{'lonDaemons'};
  my $userhome= "/home/$user" ;   my $userhome= "/home/$user" ;
Line 2617  sub ManagePermissions Line 3825  sub ManagePermissions
 #  #
 sub GetAuthType   sub GetAuthType 
 {  {
     my $domain = shift;  
     my $user   = shift;      my ($domain, $user)  = @_;
   
     Debug("GetAuthType( $domain, $user ) \n");      Debug("GetAuthType( $domain, $user ) \n");
     my $proname    = &propath($domain, $user);       my $proname    = &propath($domain, $user); 
Line 2643  sub GetAuthType Line 3851  sub GetAuthType
     }      }
 }  }
   
   #
   #  Validate a user given their domain, name and password.  This utility
   #  function is used by both  AuthenticateHandler and ChangePasswordHandler
   #  to validate the login credentials of a user.
   # Parameters:
   #    $domain    - The domain being logged into (this is required due to
   #                 the capability for multihomed systems.
   #    $user      - The name of the user being validated.
   #    $password  - The user's propoposed password.
   #
   # Returns:
   #     1        - The domain,user,pasword triplet corresponds to a valid
   #                user.
   #     0        - The domain,user,password triplet is not a valid user.
   #
   sub validate_user {
       my ($domain, $user, $password) = @_;
   
   
       # Why negative ~pi you may well ask?  Well this function is about
       # authentication, and therefore very important to get right.
       # I've initialized the flag that determines whether or not I've 
       # validated correctly to a value it's not supposed to get.
       # At the end of this function. I'll ensure that it's not still that
       # value so we don't just wind up returning some accidental value
       # as a result of executing an unforseen code path that
       # did not set $validated.
   
       my $validated = -3.14159;
   
       #  How we authenticate is determined by the type of authentication
       #  the user has been assigned.  If the authentication type is
       #  "nouser", the user does not exist so we will return 0.
   
       my $contents = &GetAuthType($domain, $user);
       my ($howpwd, $contentpwd) = split(/:/, $contents);
   
       my $null = pack("C",0); # Used by kerberos auth types.
   
       if ($howpwd ne 'nouser') {
   
    if($howpwd eq "internal") { # Encrypted is in local password file.
       $validated = (crypt($password, $contentpwd) eq $contentpwd);
    }
    elsif ($howpwd eq "unix") { # User is a normal unix user.
       $contentpwd = (getpwnam($user))[1];
       if($contentpwd) {
    if($contentpwd eq 'x') { # Shadow password file...
       my $pwauth_path = "/usr/local/sbin/pwauth";
       open PWAUTH,  "|$pwauth_path" or
    die "Cannot invoke authentication";
       print PWAUTH "$user\n$password\n";
       close PWAUTH;
       $validated = ! $?;
   
    } else {         # Passwords in /etc/passwd. 
       $validated = (crypt($password,
    $contentpwd) eq $contentpwd);
    }
       } 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 "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($krbclient);
    my $krbreturn  = &Authen::KRb5::get_in_tkt_with_password($krbclient,
    $krbserver,
    $password,
    $credentials);
    $validated = ($krbreturn == 1);
       }
       else {
    $validated = 0;
       }
    }
    elsif ($howpwd eq "localauth") { 
       #  Authenticate via installation specific authentcation method:
       $validated = &localauth::localauth($user, 
          $password, 
          $contentpwd);
    }
    else { # Unrecognized auth is also bad.
       $validated = 0;
    }
       } else {
    $validated = 0;
       }
       #
       #  $validated has the correct stat of the authentication:
       #
   
       unless ($validated != -3.14159) {
    die "ValidateUser - failed to set the value of validated";
       }
       return $validated;
   }
   
   
 sub addline {  sub addline {
     my ($fname,$hostid,$ip,$newline)=@_;      my ($fname,$hostid,$ip,$newline)=@_;
     my $contents;      my $contents;
Line 2727  sub chatadd { Line 4061  sub chatadd {
 sub unsub {  sub unsub {
     my ($fname,$clientip)=@_;      my ($fname,$clientip)=@_;
     my $result;      my $result;
       my $unsubs = 0; # Number of successful unsubscribes:
   
   
       # An old way subscriptions were handled was to have a 
       # subscription marker file:
   
       Debug("Attempting unlink of $fname.$clientname");
     if (unlink("$fname.$clientname")) {      if (unlink("$fname.$clientname")) {
  $result="ok\n";   $unsubs++; # Successful unsub via marker file.
     } else {      } 
  $result="not_subscribed\n";  
     }      # The more modern way to do it is to have a subscription list
       # file:
   
     if (-e "$fname.subscription") {      if (-e "$fname.subscription") {
  my $found=&addline($fname,$clientname,$clientip,'');   my $found=&addline($fname,$clientname,$clientip,'');
  if ($found) { $result="ok\n"; }   if ($found) { 
       $unsubs++;
    }
       } 
   
       #  If either or both of these mechanisms succeeded in unsubscribing a 
       #  resource we can return ok:
   
       if($unsubs) {
    $result = "ok\n";
     } else {      } else {
  if ($result != "ok\n") { $result="not_subscribed\n"; }   $result = "not_subscribed\n";
     }      }
   
     return $result;      return $result;
 }  }
   
Line 2859  sub make_passwd_file { Line 4212  sub make_passwd_file {
  }   }
     } elsif ($umode eq 'unix') {      } elsif ($umode eq 'unix') {
  {   {
       #
       #  Don't allow the creation of privileged accounts!!! that would
       #  be real bad!!!
       #
       my $uid = getpwnam($uname);
       if((defined $uid) && ($uid == 0)) {
    &logthis(">>>Attempted to create privilged account blocked");
    return "no_priv_account_error\n";
       }
   
     my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd";      my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd";
     {      {
  &Debug("Executing external: ".$execpath);   &Debug("Executing external: ".$execpath);
Line 2891  sub sethost { Line 4254  sub sethost {
     my (undef,$hostid)=split(/:/,$remotereq);      my (undef,$hostid)=split(/:/,$remotereq);
     if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; }      if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; }
     if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) {      if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) {
  $currenthostid=$hostid;   $currenthostid  =$hostid;
  $currentdomainid=$hostdom{$hostid};   $currentdomainid=$hostdom{$hostid};
  &logthis("Setting hostid to $hostid, and domain to $currentdomainid");   &logthis("Setting hostid to $hostid, and domain to $currentdomainid");
     } else {      } else {
Line 2931  sub userload { Line 4294  sub userload {
     return $userloadpercent;      return $userloadpercent;
 }  }
   
   # Routines for serializing arrays and hashes (copies from lonnet)
   
   sub array2str {
     my (@array) = @_;
     my $result=&arrayref2str(\@array);
     $result=~s/^__ARRAY_REF__//;
     $result=~s/__END_ARRAY_REF__$//;
     return $result;
   }
                                                                                    
   sub arrayref2str {
     my ($arrayref) = @_;
     my $result='__ARRAY_REF__';
     foreach my $elem (@$arrayref) {
       if(ref($elem) eq 'ARRAY') {
         $result.=&arrayref2str($elem).'&';
       } elsif(ref($elem) eq 'HASH') {
         $result.=&hashref2str($elem).'&';
       } elsif(ref($elem)) {
         #print("Got a ref of ".(ref($elem))." skipping.");
       } else {
         $result.=&escape($elem).'&';
       }
     }
     $result=~s/\&$//;
     $result .= '__END_ARRAY_REF__';
     return $result;
   }
                                                                                    
   sub hash2str {
     my (%hash) = @_;
     my $result=&hashref2str(\%hash);
     $result=~s/^__HASH_REF__//;
     $result=~s/__END_HASH_REF__$//;
     return $result;
   }
                                                                                    
   sub hashref2str {
     my ($hashref)=@_;
     my $result='__HASH_REF__';
     foreach (sort(keys(%$hashref))) {
       if (ref($_) eq 'ARRAY') {
         $result.=&arrayref2str($_).'=';
       } elsif (ref($_) eq 'HASH') {
         $result.=&hashref2str($_).'=';
       } elsif (ref($_)) {
         $result.='=';
         #print("Got a ref of ".(ref($_))." skipping.");
       } else {
           if ($_) {$result.=&escape($_).'=';} else { last; }
       }
   
       if(ref($hashref->{$_}) eq 'ARRAY') {
         $result.=&arrayref2str($hashref->{$_}).'&';
       } elsif(ref($hashref->{$_}) eq 'HASH') {
         $result.=&hashref2str($hashref->{$_}).'&';
       } elsif(ref($hashref->{$_})) {
          $result.='&';
         #print("Got a ref of ".(ref($hashref->{$_}))." skipping.");
       } else {
         $result.=&escape($hashref->{$_}).'&';
       }
     }
     $result=~s/\&$//;
     $result .= '__END_HASH_REF__';
     return $result;
   }
   
 # ----------------------------------- POD (plain old documentation, CPAN style)  # ----------------------------------- POD (plain old documentation, CPAN style)
   
 =head1 NAME  =head1 NAME

Removed from v.1.166  
changed lines
  Added in v.1.221


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