Annotation of loncom/lond, revision 1.208

1.1       albertel    1: #!/usr/bin/perl
                      2: # The LearningOnline Network
                      3: # lond "LON Daemon" Server (port "LOND" 5663)
1.60      www         4: #
1.208   ! albertel    5: # $Id: lond,v 1.207 2004/07/23 11:03:05 foxr Exp $
1.60      www         6: #
                      7: # Copyright Michigan State University Board of Trustees
                      8: #
                      9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                     10: #
                     11: # LON-CAPA is free software; you can redistribute it and/or modify
                     12: # it under the terms of the GNU General Public License as published by
1.167     foxr       13: # the Free Software Foundation; either version 2 of the License, or 
1.60      www        14: # (at your option) any later version.
                     15: #
                     16: # LON-CAPA is distributed in the hope that it will be useful,
                     17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     19: # GNU General Public License for more details.
                     20: #
                     21: # You should have received a copy of the GNU General Public License
                     22: # along with LON-CAPA; if not, write to the Free Software
1.178     foxr       23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1.60      www        24: #
                     25: # /home/httpd/html/adm/gpl.txt
                     26: #
1.161     foxr       27: 
                     28: 
1.60      www        29: # http://www.lon-capa.org/
                     30: #
1.54      harris41   31: 
1.134     albertel   32: use strict;
1.80      harris41   33: use lib '/home/httpd/lib/perl/';
                     34: use LONCAPA::Configuration;
                     35: 
1.1       albertel   36: use IO::Socket;
                     37: use IO::File;
1.126     albertel   38: #use Apache::File;
1.1       albertel   39: use Symbol;
                     40: use POSIX;
                     41: use Crypt::IDEA;
                     42: use LWP::UserAgent();
1.3       www        43: use GDBM_File;
                     44: use Authen::Krb4;
1.91      albertel   45: use Authen::Krb5;
1.49      albertel   46: use lib '/home/httpd/lib/perl/';
                     47: use localauth;
1.193     raeburn    48: use localenroll;
1.143     foxr       49: use File::Copy;
1.169     foxr       50: use LONCAPA::ConfigFileEdit;
1.200     matthew    51: use LONCAPA::lonlocal;
                     52: use LONCAPA::lonssl;
1.1       albertel   53: 
1.204     albertel   54: my $DEBUG = 0;		       # Non zero to enable debug log entries.
1.77      foxr       55: 
1.57      www        56: my $status='';
                     57: my $lastlog='';
                     58: 
1.208   ! albertel   59: my $VERSION='$Revision: 1.207 $'; #' stupid emacs
1.121     albertel   60: my $remoteVERSION;
1.115     albertel   61: my $currenthostid;
                     62: my $currentdomainid;
1.134     albertel   63: 
                     64: my $client;
1.200     matthew    65: my $clientip;			# IP address of client.
                     66: my $clientdns;			# DNS name of client.
                     67: my $clientname;			# LonCAPA name of client.
1.140     foxr       68: 
1.134     albertel   69: my $server;
1.200     matthew    70: my $thisserver;			# DNS of us.
                     71: 
                     72: my $keymode;
1.198     foxr       73: 
1.207     foxr       74: my $cipher;			# Cipher key negotiated with client
                     75: my $tmpsnum = 0;		# Id of tmpputs.
                     76: 
1.178     foxr       77: # 
                     78: #   Connection type is:
                     79: #      client                   - All client actions are allowed
                     80: #      manager                  - only management functions allowed.
                     81: #      both                     - Both management and client actions are allowed
                     82: #
1.161     foxr       83: 
1.178     foxr       84: my $ConnectionType;
1.161     foxr       85: 
1.200     matthew    86: my %hostid;			# ID's for hosts in cluster by ip.
                     87: my %hostdom;			# LonCAPA domain for hosts in cluster.
                     88: my %hostip;			# IPs for hosts in cluster.
                     89: my %hostdns;			# ID's of hosts looked up by DNS name.
1.161     foxr       90: 
1.178     foxr       91: my %managers;			# Ip -> manager names
1.161     foxr       92: 
1.178     foxr       93: my %perlvar;			# Will have the apache conf defined perl vars.
1.134     albertel   94: 
1.178     foxr       95: #
1.207     foxr       96: #   The hash below is used for command dispatching, and is therefore keyed on the request keyword.
                     97: #    Each element of the hash contains a reference to an array that contains:
                     98: #          A reference to a sub that executes the request corresponding to the keyword.
                     99: #          A flag that is true if the request must be encoded to be acceptable.
                    100: #          A mask with bits as follows:
                    101: #                      CLIENT_OK    - Set when the function is allowed by ordinary clients
                    102: #                      MANAGER_OK   - Set when the function is allowed to manager clients.
                    103: #
                    104: my $CLIENT_OK  = 1;
                    105: my $MANAGER_OK = 2;
                    106: my %Dispatcher;
                    107: 
                    108: 
                    109: #
1.178     foxr      110: #  The array below are password error strings."
                    111: #
                    112: my $lastpwderror    = 13;		# Largest error number from lcpasswd.
                    113: my @passwderrors = ("ok",
                    114: 		   "lcpasswd must be run as user 'www'",
                    115: 		   "lcpasswd got incorrect number of arguments",
                    116: 		   "lcpasswd did not get the right nubmer of input text lines",
                    117: 		   "lcpasswd too many simultaneous pwd changes in progress",
                    118: 		   "lcpasswd User does not exist.",
                    119: 		   "lcpasswd Incorrect current passwd",
                    120: 		   "lcpasswd Unable to su to root.",
                    121: 		   "lcpasswd Cannot set new passwd.",
                    122: 		   "lcpasswd Username has invalid characters",
                    123: 		   "lcpasswd Invalid characters in password",
                    124: 		    "11", "12",
                    125: 		    "lcpasswd Password mismatch");
1.97      foxr      126: 
                    127: 
1.178     foxr      128: #  The array below are lcuseradd error strings.:
1.97      foxr      129: 
1.178     foxr      130: my $lastadderror = 13;
                    131: my @adderrors    = ("ok",
                    132: 		    "User ID mismatch, lcuseradd must run as user www",
                    133: 		    "lcuseradd Incorrect number of command line parameters must be 3",
                    134: 		    "lcuseradd Incorrect number of stdinput lines, must be 3",
                    135: 		    "lcuseradd Too many other simultaneous pwd changes in progress",
                    136: 		    "lcuseradd User does not exist",
                    137: 		    "lcuseradd Unable to make www member of users's group",
                    138: 		    "lcuseradd Unable to su to root",
                    139: 		    "lcuseradd Unable to set password",
                    140: 		    "lcuseradd Usrname has invalid characters",
                    141: 		    "lcuseradd Password has an invalid character",
                    142: 		    "lcuseradd User already exists",
                    143: 		    "lcuseradd Could not add user.",
                    144: 		    "lcuseradd Password mismatch");
1.97      foxr      145: 
1.96      foxr      146: 
1.207     foxr      147: 
                    148: #
                    149: #   Statistics that are maintained and dislayed in the status line.
                    150: #
                    151: my $Transactions;		# Number of attempted transactions.
                    152: my $Failures;			# Number of transcations failed.
                    153: 
                    154: #   ResetStatistics: 
                    155: #      Resets the statistics counters:
                    156: #
                    157: sub ResetStatistics {
                    158:     $Transactions = 0;
                    159:     $Failures     = 0;
                    160: }
                    161: 
                    162: 
                    163: 
1.200     matthew   164: #------------------------------------------------------------------------
                    165: #
                    166: #   LocalConnection
                    167: #     Completes the formation of a locally authenticated connection.
                    168: #     This function will ensure that the 'remote' client is really the
                    169: #     local host.  If not, the connection is closed, and the function fails.
                    170: #     If so, initcmd is parsed for the name of a file containing the
                    171: #     IDEA session key.  The fie is opened, read, deleted and the session
                    172: #     key returned to the caller.
                    173: #
                    174: # Parameters:
                    175: #   $Socket      - Socket open on client.
                    176: #   $initcmd     - The full text of the init command.
                    177: #
                    178: # Implicit inputs:
                    179: #    $clientdns  - The DNS name of the remote client.
                    180: #    $thisserver - Our DNS name.
                    181: #
                    182: # Returns:
                    183: #     IDEA session key on success.
                    184: #     undef on failure.
                    185: #
                    186: sub LocalConnection {
                    187:     my ($Socket, $initcmd) = @_;
                    188:     Debug("Attempting local connection: $initcmd client: $clientdns me: $thisserver");
                    189:     if($clientdns ne $thisserver) {
                    190: 	&logthis('<font color="red"> LocalConnection rejecting non local: '
                    191: 		 ."$clientdns ne $thisserver </font>");
                    192: 	close $Socket;
                    193: 	return undef;
                    194:     } 
                    195:     else {
                    196: 	chomp($initcmd);	# Get rid of \n in filename.
                    197: 	my ($init, $type, $name) = split(/:/, $initcmd);
                    198: 	Debug(" Init command: $init $type $name ");
                    199: 
                    200: 	# Require that $init = init, and $type = local:  Otherwise
                    201: 	# the caller is insane:
                    202: 
                    203: 	if(($init ne "init") && ($type ne "local")) {
                    204: 	    &logthis('<font color = "red"> LocalConnection: caller is insane! '
                    205: 		     ."init = $init, and type = $type </font>");
                    206: 	    close($Socket);;
                    207: 	    return undef;
                    208: 		
                    209: 	}
                    210: 	#  Now get the key filename:
                    211: 
                    212: 	my $IDEAKey = lonlocal::ReadKeyFile($name);
                    213: 	return $IDEAKey;
                    214:     }
                    215: }
                    216: #------------------------------------------------------------------------------
                    217: #
                    218: #  SSLConnection
                    219: #   Completes the formation of an ssh authenticated connection. The
                    220: #   socket is promoted to an ssl socket.  If this promotion and the associated
                    221: #   certificate exchange are successful, the IDEA key is generated and sent
                    222: #   to the remote peer via the SSL tunnel. The IDEA key is also returned to
                    223: #   the caller after the SSL tunnel is torn down.
                    224: #
                    225: # Parameters:
                    226: #   Name              Type             Purpose
                    227: #   $Socket          IO::Socket::INET  Plaintext socket.
                    228: #
                    229: # Returns:
                    230: #    IDEA key on success.
                    231: #    undef on failure.
                    232: #
                    233: sub SSLConnection {
                    234:     my $Socket   = shift;
                    235: 
                    236:     Debug("SSLConnection: ");
                    237:     my $KeyFile         = lonssl::KeyFile();
                    238:     if(!$KeyFile) {
                    239: 	my $err = lonssl::LastError();
                    240: 	&logthis("<font color=\"red\"> CRITICAL"
                    241: 		 ."Can't get key file $err </font>");
                    242: 	return undef;
                    243:     }
                    244:     my ($CACertificate,
                    245: 	$Certificate) = lonssl::CertificateFile();
                    246: 
                    247: 
                    248:     # If any of the key, certificate or certificate authority 
                    249:     # certificate filenames are not defined, this can't work.
                    250: 
                    251:     if((!$Certificate) || (!$CACertificate)) {
                    252: 	my $err = lonssl::LastError();
                    253: 	&logthis("<font color=\"red\"> CRITICAL"
                    254: 		 ."Can't get certificates: $err </font>");
                    255: 
                    256: 	return undef;
                    257:     }
                    258:     Debug("Key: $KeyFile CA: $CACertificate Cert: $Certificate");
                    259: 
                    260:     # Indicate to our peer that we can procede with
                    261:     # a transition to ssl authentication:
                    262: 
                    263:     print $Socket "ok:ssl\n";
                    264: 
                    265:     Debug("Approving promotion -> ssl");
                    266:     #  And do so:
                    267: 
                    268:     my $SSLSocket = lonssl::PromoteServerSocket($Socket,
                    269: 						$CACertificate,
                    270: 						$Certificate,
                    271: 						$KeyFile);
                    272:     if(! ($SSLSocket) ) {	# SSL socket promotion failed.
                    273: 	my $err = lonssl::LastError();
                    274: 	&logthis("<font color=\"red\"> CRITICAL "
                    275: 		 ."SSL Socket promotion failed: $err </font>");
                    276: 	return undef;
                    277:     }
                    278:     Debug("SSL Promotion successful");
                    279: 
                    280:     # 
                    281:     #  The only thing we'll use the socket for is to send the IDEA key
                    282:     #  to the peer:
                    283: 
                    284:     my $Key = lonlocal::CreateCipherKey();
                    285:     print $SSLSocket "$Key\n";
                    286: 
                    287:     lonssl::Close($SSLSocket); 
                    288: 
                    289:     Debug("Key exchange complete: $Key");
                    290: 
                    291:     return $Key;
                    292: }
                    293: #
                    294: #     InsecureConnection: 
                    295: #        If insecure connections are allowd,
                    296: #        exchange a challenge with the client to 'validate' the
                    297: #        client (not really, but that's the protocol):
                    298: #        We produce a challenge string that's sent to the client.
                    299: #        The client must then echo the challenge verbatim to us.
                    300: #
                    301: #  Parameter:
                    302: #      Socket      - Socket open on the client.
                    303: #  Returns:
                    304: #      1           - success.
                    305: #      0           - failure (e.g.mismatch or insecure not allowed).
                    306: #
                    307: sub InsecureConnection {
                    308:     my $Socket  =  shift;
                    309: 
                    310:     #   Don't even start if insecure connections are not allowed.
                    311: 
                    312:     if(! $perlvar{londAllowInsecure}) {	# Insecure connections not allowed.
                    313: 	return 0;
                    314:     }
                    315: 
                    316:     #   Fabricate a challenge string and send it..
                    317: 
                    318:     my $challenge = "$$".time;	# pid + time.
                    319:     print $Socket "$challenge\n";
                    320:     &status("Waiting for challenge reply");
                    321: 
                    322:     my $answer = <$Socket>;
                    323:     $answer    =~s/\W//g;
                    324:     if($challenge eq $answer) {
                    325: 	return 1;
                    326:     } 
                    327:     else {
                    328: 	logthis("<font color='blue'>WARNING client did not respond to challenge</font>");
                    329: 	&status("No challenge reqply");
                    330: 	return 0;
                    331:     }
                    332:     
                    333: 
                    334: }
                    335: 
1.96      foxr      336: #
1.140     foxr      337: #   GetCertificate: Given a transaction that requires a certificate,
                    338: #   this function will extract the certificate from the transaction
                    339: #   request.  Note that at this point, the only concept of a certificate
                    340: #   is the hostname to which we are connected.
                    341: #
                    342: #   Parameter:
                    343: #      request   - The request sent by our client (this parameterization may
                    344: #                  need to change when we really use a certificate granting
                    345: #                  authority.
                    346: #
                    347: sub GetCertificate {
                    348:     my $request = shift;
                    349: 
                    350:     return $clientip;
                    351: }
1.161     foxr      352: 
1.178     foxr      353: #
                    354: #   Return true if client is a manager.
                    355: #
                    356: sub isManager {
                    357:     return (($ConnectionType eq "manager") || ($ConnectionType eq "both"));
                    358: }
                    359: #
                    360: #   Return tru if client can do client functions
                    361: #
                    362: sub isClient {
                    363:     return (($ConnectionType eq "client") || ($ConnectionType eq "both"));
                    364: }
1.161     foxr      365: 
                    366: 
1.156     foxr      367: #
                    368: #   ReadManagerTable: Reads in the current manager table. For now this is
                    369: #                     done on each manager authentication because:
                    370: #                     - These authentications are not frequent
                    371: #                     - This allows dynamic changes to the manager table
                    372: #                       without the need to signal to the lond.
                    373: #
                    374: 
                    375: sub ReadManagerTable {
                    376: 
                    377:     #   Clean out the old table first..
                    378: 
1.166     foxr      379:    foreach my $key (keys %managers) {
                    380:       delete $managers{$key};
                    381:    }
                    382: 
                    383:    my $tablename = $perlvar{'lonTabDir'}."/managers.tab";
                    384:    if (!open (MANAGERS, $tablename)) {
                    385:       logthis('<font color="red">No manager table.  Nobody can manage!!</font>');
                    386:       return;
                    387:    }
                    388:    while(my $host = <MANAGERS>) {
                    389:       chomp($host);
                    390:       if ($host =~ "^#") {                  # Comment line.
                    391:          next;
                    392:       }
                    393:       if (!defined $hostip{$host}) { # This is a non cluster member
1.161     foxr      394: 	    #  The entry is of the form:
                    395: 	    #    cluname:hostname
                    396: 	    #  cluname - A 'cluster hostname' is needed in order to negotiate
                    397: 	    #            the host key.
                    398: 	    #  hostname- The dns name of the host.
                    399: 	    #
1.166     foxr      400:           my($cluname, $dnsname) = split(/:/, $host);
                    401:           
                    402:           my $ip = gethostbyname($dnsname);
                    403:           if(defined($ip)) {                 # bad names don't deserve entry.
                    404:             my $hostip = inet_ntoa($ip);
                    405:             $managers{$hostip} = $cluname;
                    406:             logthis('<font color="green"> registering manager '.
                    407:                     "$dnsname as $cluname with $hostip </font>\n");
                    408:          }
                    409:       } else {
                    410:          logthis('<font color="green"> existing host'." $host</font>\n");
                    411:          $managers{$hostip{$host}} = $host;  # Use info from cluster tab if clumemeber
                    412:       }
                    413:    }
1.156     foxr      414: }
1.140     foxr      415: 
                    416: #
                    417: #  ValidManager: Determines if a given certificate represents a valid manager.
                    418: #                in this primitive implementation, the 'certificate' is
                    419: #                just the connecting loncapa client name.  This is checked
                    420: #                against a valid client list in the configuration.
                    421: #
                    422: #                  
                    423: sub ValidManager {
                    424:     my $certificate = shift; 
                    425: 
1.163     foxr      426:     return isManager;
1.140     foxr      427: }
                    428: #
1.143     foxr      429: #  CopyFile:  Called as part of the process of installing a 
                    430: #             new configuration file.  This function copies an existing
                    431: #             file to a backup file.
                    432: # Parameters:
                    433: #     oldfile  - Name of the file to backup.
                    434: #     newfile  - Name of the backup file.
                    435: # Return:
                    436: #     0   - Failure (errno has failure reason).
                    437: #     1   - Success.
                    438: #
                    439: sub CopyFile {
1.192     foxr      440: 
                    441:     my ($oldfile, $newfile) = @_;
1.143     foxr      442: 
                    443:     #  The file must exist:
                    444: 
                    445:     if(-e $oldfile) {
                    446: 
                    447: 	 # Read the old file.
                    448: 
                    449: 	my $oldfh = IO::File->new("< $oldfile");
                    450: 	if(!$oldfh) {
                    451: 	    return 0;
                    452: 	}
                    453: 	my @contents = <$oldfh>;  # Suck in the entire file.
                    454: 
                    455: 	# write the backup file:
                    456: 
                    457: 	my $newfh = IO::File->new("> $newfile");
                    458: 	if(!(defined $newfh)){
                    459: 	    return 0;
                    460: 	}
                    461: 	my $lines = scalar @contents;
                    462: 	for (my $i =0; $i < $lines; $i++) {
                    463: 	    print $newfh ($contents[$i]);
                    464: 	}
                    465: 
                    466: 	$oldfh->close;
                    467: 	$newfh->close;
                    468: 
                    469: 	chmod(0660, $newfile);
                    470: 
                    471: 	return 1;
                    472: 	    
                    473:     } else {
                    474: 	return 0;
                    475:     }
                    476: }
1.157     foxr      477: #
                    478: #  Host files are passed out with externally visible host IPs.
                    479: #  If, for example, we are behind a fire-wall or NAT host, our 
                    480: #  internally visible IP may be different than the externally
                    481: #  visible IP.  Therefore, we always adjust the contents of the
                    482: #  host file so that the entry for ME is the IP that we believe
                    483: #  we have.  At present, this is defined as the entry that
                    484: #  DNS has for us.  If by some chance we are not able to get a
                    485: #  DNS translation for us, then we assume that the host.tab file
                    486: #  is correct.  
                    487: #    BUGBUGBUG - in the future, we really should see if we can
                    488: #       easily query the interface(s) instead.
                    489: # Parameter(s):
                    490: #     contents    - The contents of the host.tab to check.
                    491: # Returns:
                    492: #     newcontents - The adjusted contents.
                    493: #
                    494: #
                    495: sub AdjustHostContents {
                    496:     my $contents  = shift;
                    497:     my $adjusted;
                    498:     my $me        = $perlvar{'lonHostID'};
                    499: 
1.166     foxr      500:  foreach my $line (split(/\n/,$contents)) {
1.157     foxr      501: 	if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/))) {
                    502: 	    chomp($line);
                    503: 	    my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon)=split(/:/,$line);
                    504: 	    if ($id eq $me) {
1.166     foxr      505:           my $ip = gethostbyname($name);
                    506:           my $ipnew = inet_ntoa($ip);
                    507:          $ip = $ipnew;
1.157     foxr      508: 		#  Reconstruct the host line and append to adjusted:
                    509: 		
1.166     foxr      510: 		   my $newline = "$id:$domain:$role:$name:$ip";
                    511: 		   if($maxcon ne "") { # Not all hosts have loncnew tuning params
                    512: 		     $newline .= ":$maxcon:$idleto:$mincon";
                    513: 		   }
                    514: 		   $adjusted .= $newline."\n";
1.157     foxr      515: 		
1.166     foxr      516:       } else {		# Not me, pass unmodified.
                    517: 		   $adjusted .= $line."\n";
                    518:       }
1.157     foxr      519: 	} else {                  # Blank or comment never re-written.
                    520: 	    $adjusted .= $line."\n";	# Pass blanks and comments as is.
                    521: 	}
1.166     foxr      522:  }
                    523:  return $adjusted;
1.157     foxr      524: }
1.143     foxr      525: #
                    526: #   InstallFile: Called to install an administrative file:
                    527: #       - The file is created with <name>.tmp
                    528: #       - The <name>.tmp file is then mv'd to <name>
                    529: #   This lugubrious procedure is done to ensure that we are never without
                    530: #   a valid, even if dated, version of the file regardless of who crashes
                    531: #   and when the crash occurs.
                    532: #
                    533: #  Parameters:
                    534: #       Name of the file
                    535: #       File Contents.
                    536: #  Return:
                    537: #      nonzero - success.
                    538: #      0       - failure and $! has an errno.
                    539: #
                    540: sub InstallFile {
1.192     foxr      541: 
                    542:     my ($Filename, $Contents) = @_;
1.143     foxr      543:     my $TempFile = $Filename.".tmp";
                    544: 
                    545:     #  Open the file for write:
                    546: 
                    547:     my $fh = IO::File->new("> $TempFile"); # Write to temp.
                    548:     if(!(defined $fh)) {
                    549: 	&logthis('<font color="red"> Unable to create '.$TempFile."</font>");
                    550: 	return 0;
                    551:     }
                    552:     #  write the contents of the file:
                    553: 
                    554:     print $fh ($Contents); 
                    555:     $fh->close;			# In case we ever have a filesystem w. locking
                    556: 
                    557:     chmod(0660, $TempFile);
                    558: 
                    559:     # Now we can move install the file in position.
                    560:     
                    561:     move($TempFile, $Filename);
                    562: 
                    563:     return 1;
                    564: }
1.200     matthew   565: 
                    566: 
1.169     foxr      567: #
                    568: #   ConfigFileFromSelector: converts a configuration file selector
                    569: #                 (one of host or domain at this point) into a 
                    570: #                 configuration file pathname.
                    571: #
                    572: #  Parameters:
                    573: #      selector  - Configuration file selector.
                    574: #  Returns:
                    575: #      Full path to the file or undef if the selector is invalid.
                    576: #
                    577: sub ConfigFileFromSelector {
                    578:     my $selector   = shift;
                    579:     my $tablefile;
                    580: 
                    581:     my $tabledir = $perlvar{'lonTabDir'}.'/';
                    582:     if ($selector eq "hosts") {
                    583: 	$tablefile = $tabledir."hosts.tab";
                    584:     } elsif ($selector eq "domain") {
                    585: 	$tablefile = $tabledir."domain.tab";
                    586:     } else {
                    587: 	return undef;
                    588:     }
                    589:     return $tablefile;
1.143     foxr      590: 
1.169     foxr      591: }
1.143     foxr      592: #
1.141     foxr      593: #   PushFile:  Called to do an administrative push of a file.
                    594: #              - Ensure the file being pushed is one we support.
                    595: #              - Backup the old file to <filename.saved>
                    596: #              - Separate the contents of the new file out from the
                    597: #                rest of the request.
                    598: #              - Write the new file.
                    599: #  Parameter:
                    600: #     Request - The entire user request.  This consists of a : separated
                    601: #               string pushfile:tablename:contents.
                    602: #     NOTE:  The contents may have :'s in it as well making things a bit
                    603: #            more interesting... but not much.
                    604: #  Returns:
                    605: #     String to send to client ("ok" or "refused" if bad file).
                    606: #
                    607: sub PushFile {
                    608:     my $request = shift;    
                    609:     my ($command, $filename, $contents) = split(":", $request, 3);
                    610:     
                    611:     #  At this point in time, pushes for only the following tables are
                    612:     #  supported:
                    613:     #   hosts.tab  ($filename eq host).
                    614:     #   domain.tab ($filename eq domain).
                    615:     # Construct the destination filename or reject the request.
                    616:     #
                    617:     # lonManage is supposed to ensure this, however this session could be
                    618:     # part of some elaborate spoof that managed somehow to authenticate.
                    619:     #
                    620: 
1.169     foxr      621: 
                    622:     my $tablefile = ConfigFileFromSelector($filename);
                    623:     if(! (defined $tablefile)) {
1.141     foxr      624: 	return "refused";
                    625:     }
                    626:     #
                    627:     # >copy< the old table to the backup table
                    628:     #        don't rename in case system crashes/reboots etc. in the time
                    629:     #        window between a rename and write.
                    630:     #
                    631:     my $backupfile = $tablefile;
                    632:     $backupfile    =~ s/\.tab$/.old/;
1.143     foxr      633:     if(!CopyFile($tablefile, $backupfile)) {
                    634: 	&logthis('<font color="green"> CopyFile from '.$tablefile." to ".$backupfile." failed </font>");
                    635: 	return "error:$!";
                    636:     }
1.141     foxr      637:     &logthis('<font color="green"> Pushfile: backed up '
                    638: 	    .$tablefile." to $backupfile</font>");
                    639:     
1.157     foxr      640:     #  If the file being pushed is the host file, we adjust the entry for ourself so that the
                    641:     #  IP will be our current IP as looked up in dns.  Note this is only 99% good as it's possible
                    642:     #  to conceive of conditions where we don't have a DNS entry locally.  This is possible in a 
                    643:     #  network sense but it doesn't make much sense in a LonCAPA sense so we ignore (for now)
                    644:     #  that possibilty.
                    645: 
                    646:     if($filename eq "host") {
                    647: 	$contents = AdjustHostContents($contents);
                    648:     }
                    649: 
1.141     foxr      650:     #  Install the new file:
                    651: 
1.143     foxr      652:     if(!InstallFile($tablefile, $contents)) {
                    653: 	&logthis('<font color="red"> Pushfile: unable to install '
1.145     foxr      654: 	 .$tablefile." $! </font>");
1.143     foxr      655: 	return "error:$!";
                    656:     }
                    657:     else {
                    658: 	&logthis('<font color="green"> Installed new '.$tablefile
                    659: 		 ."</font>");
                    660: 
                    661:     }
                    662: 
1.141     foxr      663: 
                    664:     #  Indicate success:
                    665:  
                    666:     return "ok";
                    667: 
                    668: }
1.145     foxr      669: 
                    670: #
                    671: #  Called to re-init either lonc or lond.
                    672: #
                    673: #  Parameters:
                    674: #    request   - The full request by the client.  This is of the form
                    675: #                reinit:<process>  
                    676: #                where <process> is allowed to be either of 
                    677: #                lonc or lond
                    678: #
                    679: #  Returns:
                    680: #     The string to be sent back to the client either:
                    681: #   ok         - Everything worked just fine.
                    682: #   error:why  - There was a failure and why describes the reason.
                    683: #
                    684: #
                    685: sub ReinitProcess {
                    686:     my $request = shift;
                    687: 
1.146     foxr      688: 
                    689:     # separate the request (reinit) from the process identifier and
                    690:     # validate it producing the name of the .pid file for the process.
                    691:     #
                    692:     #
                    693:     my ($junk, $process) = split(":", $request);
1.147     foxr      694:     my $processpidfile = $perlvar{'lonDaemons'}.'/logs/';
1.146     foxr      695:     if($process eq 'lonc') {
                    696: 	$processpidfile = $processpidfile."lonc.pid";
1.147     foxr      697: 	if (!open(PIDFILE, "< $processpidfile")) {
                    698: 	    return "error:Open failed for $processpidfile";
                    699: 	}
                    700: 	my $loncpid = <PIDFILE>;
                    701: 	close(PIDFILE);
                    702: 	logthis('<font color="red"> Reinitializing lonc pid='.$loncpid
                    703: 		."</font>");
                    704: 	kill("USR2", $loncpid);
1.146     foxr      705:     } elsif ($process eq 'lond') {
1.147     foxr      706: 	logthis('<font color="red"> Reinitializing self (lond) </font>');
                    707: 	&UpdateHosts;			# Lond is us!!
1.146     foxr      708:     } else {
                    709: 	&logthis('<font color="yellow" Invalid reinit request for '.$process
                    710: 		 ."</font>");
                    711: 	return "error:Invalid process identifier $process";
                    712:     }
1.145     foxr      713:     return 'ok';
                    714: }
1.168     foxr      715: #   Validate a line in a configuration file edit script:
                    716: #   Validation includes:
                    717: #     - Ensuring the command is valid.
                    718: #     - Ensuring the command has sufficient parameters
                    719: #   Parameters:
                    720: #     scriptline - A line to validate (\n has been stripped for what it's worth).
1.167     foxr      721: #
1.168     foxr      722: #   Return:
                    723: #      0     - Invalid scriptline.
                    724: #      1     - Valid scriptline
                    725: #  NOTE:
                    726: #     Only the command syntax is checked, not the executability of the
                    727: #     command.
                    728: #
                    729: sub isValidEditCommand {
                    730:     my $scriptline = shift;
                    731: 
                    732:     #   Line elements are pipe separated:
                    733: 
                    734:     my ($command, $key, $newline)  = split(/\|/, $scriptline);
                    735:     &logthis('<font color="green"> isValideditCommand checking: '.
                    736: 	     "Command = '$command', Key = '$key', Newline = '$newline' </font>\n");
                    737:     
                    738:     if ($command eq "delete") {
                    739: 	#
                    740: 	#   key with no newline.
                    741: 	#
                    742: 	if( ($key eq "") || ($newline ne "")) {
                    743: 	    return 0;		# Must have key but no newline.
                    744: 	} else {
                    745: 	    return 1;		# Valid syntax.
                    746: 	}
1.169     foxr      747:     } elsif ($command eq "replace") {
1.168     foxr      748: 	#
                    749: 	#   key and newline:
                    750: 	#
                    751: 	if (($key eq "") || ($newline eq "")) {
                    752: 	    return 0;
                    753: 	} else {
                    754: 	    return 1;
                    755: 	}
1.169     foxr      756:     } elsif ($command eq "append") {
                    757: 	if (($key ne "") && ($newline eq "")) {
                    758: 	    return 1;
                    759: 	} else {
                    760: 	    return 0;
                    761: 	}
1.168     foxr      762:     } else {
                    763: 	return 0;		# Invalid command.
                    764:     }
                    765:     return 0;			# Should not get here!!!
                    766: }
1.169     foxr      767: #
                    768: #   ApplyEdit - Applies an edit command to a line in a configuration 
                    769: #               file.  It is the caller's responsiblity to validate the
                    770: #               edit line.
                    771: #   Parameters:
                    772: #      $directive - A single edit directive to apply.  
                    773: #                   Edit directives are of the form:
                    774: #                  append|newline      - Appends a new line to the file.
                    775: #                  replace|key|newline - Replaces the line with key value 'key'
                    776: #                  delete|key          - Deletes the line with key value 'key'.
                    777: #      $editor   - A config file editor object that contains the
                    778: #                  file being edited.
                    779: #
                    780: sub ApplyEdit {
1.192     foxr      781: 
                    782:     my ($directive, $editor) = @_;
1.169     foxr      783: 
                    784:     # Break the directive down into its command and its parameters
                    785:     # (at most two at this point.  The meaning of the parameters, if in fact
                    786:     #  they exist depends on the command).
                    787: 
                    788:     my ($command, $p1, $p2) = split(/\|/, $directive);
                    789: 
                    790:     if($command eq "append") {
                    791: 	$editor->Append($p1);	          # p1 - key p2 null.
                    792:     } elsif ($command eq "replace") {
                    793: 	$editor->ReplaceLine($p1, $p2);   # p1 - key p2 = newline.
                    794:     } elsif ($command eq "delete") {
                    795: 	$editor->DeleteLine($p1);         # p1 - key p2 null.
                    796:     } else {			          # Should not get here!!!
                    797: 	die "Invalid command given to ApplyEdit $command"
                    798:     }
                    799: }
                    800: #
                    801: # AdjustOurHost:
                    802: #           Adjusts a host file stored in a configuration file editor object
                    803: #           for the true IP address of this host. This is necessary for hosts
                    804: #           that live behind a firewall.
                    805: #           Those hosts have a publicly distributed IP of the firewall, but
                    806: #           internally must use their actual IP.  We assume that a given
                    807: #           host only has a single IP interface for now.
                    808: # Formal Parameters:
                    809: #     editor   - The configuration file editor to adjust.  This
                    810: #                editor is assumed to contain a hosts.tab file.
                    811: # Strategy:
                    812: #    - Figure out our hostname.
                    813: #    - Lookup the entry for this host.
                    814: #    - Modify the line to contain our IP
                    815: #    - Do a replace for this host.
                    816: sub AdjustOurHost {
                    817:     my $editor        = shift;
                    818: 
                    819:     # figure out who I am.
                    820: 
                    821:     my $myHostName    = $perlvar{'lonHostID'}; # LonCAPA hostname.
                    822: 
                    823:     #  Get my host file entry.
                    824: 
                    825:     my $ConfigLine    = $editor->Find($myHostName);
                    826:     if(! (defined $ConfigLine)) {
                    827: 	die "AdjustOurHost - no entry for me in hosts file $myHostName";
                    828:     }
                    829:     # figure out my IP:
                    830:     #   Use the config line to get my hostname.
                    831:     #   Use gethostbyname to translate that into an IP address.
                    832:     #
                    833:     my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon) = split(/:/,$ConfigLine);
                    834:     my $BinaryIp = gethostbyname($name);
                    835:     my $ip       = inet_ntoa($ip);
                    836:     #
                    837:     #  Reassemble the config line from the elements in the list.
                    838:     #  Note that if the loncnew items were not present before, they will
                    839:     #  be now even if they would be empty
                    840:     #
                    841:     my $newConfigLine = $id;
                    842:     foreach my $item ($domain, $role, $name, $ip, $maxcon, $idleto, $mincon) {
                    843: 	$newConfigLine .= ":".$item;
                    844:     }
                    845:     #  Replace the line:
                    846: 
                    847:     $editor->ReplaceLine($id, $newConfigLine);
                    848:     
                    849: }
                    850: #
                    851: #   ReplaceConfigFile:
                    852: #              Replaces a configuration file with the contents of a
                    853: #              configuration file editor object.
                    854: #              This is done by:
                    855: #              - Copying the target file to <filename>.old
                    856: #              - Writing the new file to <filename>.tmp
                    857: #              - Moving <filename.tmp>  -> <filename>
                    858: #              This laborious process ensures that the system is never without
                    859: #              a configuration file that's at least valid (even if the contents
                    860: #              may be dated).
                    861: #   Parameters:
                    862: #        filename   - Name of the file to modify... this is a full path.
                    863: #        editor     - Editor containing the file.
                    864: #
                    865: sub ReplaceConfigFile {
1.192     foxr      866:     
                    867:     my ($filename, $editor) = @_;
1.168     foxr      868: 
1.169     foxr      869:     CopyFile ($filename, $filename.".old");
                    870: 
                    871:     my $contents  = $editor->Get(); # Get the contents of the file.
                    872: 
                    873:     InstallFile($filename, $contents);
                    874: }
1.168     foxr      875: #   
                    876: #
                    877: #   Called to edit a configuration table  file
1.167     foxr      878: #   Parameters:
                    879: #      request           - The entire command/request sent by lonc or lonManage
                    880: #   Return:
                    881: #      The reply to send to the client.
1.168     foxr      882: #
1.167     foxr      883: sub EditFile {
                    884:     my $request = shift;
                    885: 
                    886:     #  Split the command into it's pieces:  edit:filetype:script
                    887: 
1.168     foxr      888:     my ($request, $filetype, $script) = split(/:/, $request,3);	# : in script
1.167     foxr      889: 
                    890:     #  Check the pre-coditions for success:
                    891: 
                    892:     if($request != "edit") {	# Something is amiss afoot alack.
                    893: 	return "error:edit request detected, but request != 'edit'\n";
                    894:     }
                    895:     if( ($filetype ne "hosts")  &&
                    896: 	($filetype ne "domain")) {
                    897: 	return "error:edit requested with invalid file specifier: $filetype \n";
                    898:     }
                    899: 
                    900:     #   Split the edit script and check it's validity.
1.168     foxr      901: 
                    902:     my @scriptlines = split(/\n/, $script);  # one line per element.
                    903:     my $linecount   = scalar(@scriptlines);
                    904:     for(my $i = 0; $i < $linecount; $i++) {
                    905: 	chomp($scriptlines[$i]);
                    906: 	if(!isValidEditCommand($scriptlines[$i])) {
                    907: 	    return "error:edit with bad script line: '$scriptlines[$i]' \n";
                    908: 	}
                    909:     }
1.145     foxr      910: 
1.167     foxr      911:     #   Execute the edit operation.
1.169     foxr      912:     #   - Create a config file editor for the appropriate file and 
                    913:     #   - execute each command in the script:
                    914:     #
                    915:     my $configfile = ConfigFileFromSelector($filetype);
                    916:     if (!(defined $configfile)) {
                    917: 	return "refused\n";
                    918:     }
                    919:     my $editor = ConfigFileEdit->new($configfile);
1.167     foxr      920: 
1.169     foxr      921:     for (my $i = 0; $i < $linecount; $i++) {
                    922: 	ApplyEdit($scriptlines[$i], $editor);
                    923:     }
                    924:     # If the file is the host file, ensure that our host is
                    925:     # adjusted to have our ip:
                    926:     #
                    927:     if($filetype eq "host") {
                    928: 	AdjustOurHost($editor);
                    929:     }
                    930:     #  Finally replace the current file with our file.
                    931:     #
                    932:     ReplaceConfigFile($configfile, $editor);
1.167     foxr      933: 
                    934:     return "ok\n";
                    935: }
1.207     foxr      936: 
                    937: #---------------------------------------------------------------
                    938: #
                    939: # Manipulation of hash based databases (factoring out common code
                    940: # for later use as we refactor.
                    941: #
                    942: #  Ties a domain level resource file to a hash.
                    943: #  If requested a history entry is created in the associated hist file.
                    944: #
                    945: #  Parameters:
                    946: #     domain    - Name of the domain in which the resource file lives.
                    947: #     namespace - Name of the hash within that domain.
                    948: #     how       - How to tie the hash (e.g. GDBM_WRCREAT()).
                    949: #     loghead   - Optional parameter, if present a log entry is created
                    950: #                 in the associated history file and this is the first part
                    951: #                  of that entry.
                    952: #     logtail   - Goes along with loghead,  The actual logentry is of the
                    953: #                 form $loghead:<timestamp>:logtail.
                    954: # Returns:
                    955: #    Reference to a hash bound to the db file or alternatively undef
                    956: #    if the tie failed.
                    957: #
                    958: sub TieDomainHash {
                    959:     my ($domain, 
                    960: 	$namespace,
                    961: 	$how)     = @_;
                    962:     
                    963:     # Filter out any whitespace in the domain name:
                    964:     
                    965:     $domain =~ s/\W//g;
                    966:     
                    967:     # We have enough to go on to tie the hash:
                    968:     
                    969:     my $user_top_dir   = $perlvar{'lonUsersDir'};
                    970:     my $domain_dir     = $user_top_dir."/$domain";
                    971:     my $resource_file  = $domain_dir."/$namespace.db";
                    972:     my %hash;
                    973:     if(tie(%hash, 'GDBM_File', $resource_file, $how, 0640)) {
                    974: 	if (scalar @_) {	# Need to log the operation.
                    975: 	    my $logFh = IO::File->new(">>domain_dir/$namespace.hist");
                    976: 	    if($logFh) {
                    977: 		my $timestamp = time;
                    978: 		my ($loghead, $logtail) = @_;
                    979: 		print $logFh "$loghead:$timestamp:$logtail\n";
                    980: 	    }
                    981: 	}
                    982: 	return \%hash;		# Return the tied hash.
                    983:     }
                    984:     else {
                    985: 	return undef;		# Tie failed.
                    986:     }
                    987: }
                    988: 
                    989: #
                    990: #   Ties a user's resource file to a hash.  
                    991: #   If necessary, an appropriate history
                    992: #   log file entry is made as well.
                    993: #   This sub factors out common code from the subs that manipulate
                    994: #   the various gdbm files that keep keyword value pairs.
                    995: # Parameters:
                    996: #   domain       - Name of the domain the user is in.
                    997: #   user         - Name of the 'current user'.
                    998: #   namespace    - Namespace representing the file to tie.
                    999: #   how          - What the tie is done to (e.g. GDBM_WRCREAT().
                   1000: #   loghead      - Optional first part of log entry if there may be a
                   1001: #                  history file.
                   1002: #   what         - Optional tail of log entry if there may be a history
                   1003: #                  file.
                   1004: # Returns:
                   1005: #   hash to which the database is tied.  It's up to the caller to untie.
                   1006: #   undef if the has could not be tied.
                   1007: #
                   1008: sub TieUserHash {
                   1009:     my ($domain,
                   1010: 	$user,
                   1011: 	$namespace,
                   1012: 	$how)       = @_;
                   1013: 
                   1014:     
                   1015:     $namespace=~s/\//\_/g;	# / -> _
                   1016:     $namespace=~s/\W//g;		# whitespace eliminated.
                   1017:     my $proname     = propath($domain, $user);
                   1018:    
                   1019:     # If this is a namespace for which a history is kept,
                   1020:     # make the history log entry:
                   1021:     
                   1022:     
                   1023:     if (($namespace =~/^nohist\_/) && (scalar @_ > 0)) {
                   1024: 	my $args = scalar @_;
                   1025: 	Debug(" Opening history: $namespace $args");
                   1026: 	my $hfh = IO::File->new(">>$proname/$namespace.hist"); 
                   1027: 	if($hfh) {
                   1028: 	    my $now = time;
                   1029: 	    my $loghead  = shift;
                   1030: 	    my $what    = shift;
                   1031: 	    print $hfh "$loghead:$now:$what\n";
                   1032: 	}
                   1033:     }
                   1034:     #  Tie the database.
                   1035:     
                   1036:     my %hash;
                   1037:     if(tie(%hash, 'GDBM_File', "$proname/$namespace.db",
                   1038: 	   $how, 0640)) {
                   1039: 	return \%hash;
                   1040:     }
                   1041:     else {
                   1042: 	return undef;
                   1043:     }
                   1044:     
                   1045: }
                   1046: #---------------------------------------------------------------
                   1047: #
                   1048: #   Getting, decoding and dispatching requests:
                   1049: #
                   1050: 
                   1051: #
                   1052: #   Get a Request:
                   1053: #   Gets a Request message from the client.  The transaction
                   1054: #   is defined as a 'line' of text.  We remove the new line
                   1055: #   from the text line.  
                   1056: #   
                   1057: sub GetRequest {
                   1058:     my $input = <$client>;
                   1059:     chomp($input);
                   1060: 
                   1061:     Debug("Request = $input\n");
                   1062: 
                   1063:     &status('Processing '.$clientname.':'.$input);
                   1064: 
                   1065:     return $input;
                   1066: }
                   1067: #
                   1068: #   Decipher encoded traffic
                   1069: #  Parameters:
                   1070: #     input      - Encoded data.
                   1071: #  Returns:
                   1072: #     Decoded data or undef if encryption key was not yet negotiated.
                   1073: #  Implicit input:
                   1074: #     cipher  - This global holds the negotiated encryption key.
                   1075: #
                   1076: sub Decipher {
                   1077:     my ($input)  = @_;
                   1078:     my $output = '';
                   1079:    
                   1080:    
                   1081:     if($cipher) {
                   1082: 	my($enc, $enclength, $encinput) = split(/:/, $input);
                   1083: 	for(my $encidx = 0; $encidx < length($encinput); $encidx += 16) {
                   1084: 	    $output .= 
                   1085: 		$cipher->decrypt(pack("H16", substr($encinput, $encidx, 16)));
                   1086: 	}
                   1087: 	return substr($output, 0, $enclength);
                   1088:     } else {
                   1089: 	return undef;
                   1090:     }
                   1091: }
                   1092: 
                   1093: #
                   1094: #   Register a command processor.  This function is invoked to register a sub
                   1095: #   to process a request.  Once registered, the ProcessRequest sub can automatically
                   1096: #   dispatch requests to an appropriate sub, and do the top level validity checking
                   1097: #   as well:
                   1098: #    - Is the keyword recognized.
                   1099: #    - Is the proper client type attempting the request.
                   1100: #    - Is the request encrypted if it has to be.
                   1101: #   Parameters:
                   1102: #    $request_name         - Name of the request being registered.
                   1103: #                           This is the command request that will match
                   1104: #                           against the hash keywords to lookup the information
                   1105: #                           associated with the dispatch information.
                   1106: #    $procedure           - Reference to a sub to call to process the request.
                   1107: #                           All subs get called as follows:
                   1108: #                             Procedure($cmd, $tail, $replyfd, $key)
                   1109: #                             $cmd    - the actual keyword that invoked us.
                   1110: #                             $tail   - the tail of the request that invoked us.
                   1111: #                             $replyfd- File descriptor connected to the client
                   1112: #    $must_encode          - True if the request must be encoded to be good.
                   1113: #    $client_ok            - True if it's ok for a client to request this.
                   1114: #    $manager_ok           - True if it's ok for a manager to request this.
                   1115: # Side effects:
                   1116: #      - On success, the Dispatcher hash has an entry added for the key $RequestName
                   1117: #      - On failure, the program will die as it's a bad internal bug to try to 
                   1118: #        register a duplicate command handler.
                   1119: #
                   1120: sub RegisterHandler {
                   1121:     my ($request_name,
                   1122: 	$procedure,
                   1123: 	$must_encode,
                   1124: 	$client_ok,
                   1125: 	$manager_ok)   = @_;
                   1126: 
                   1127:     #  Don't allow duplication#
                   1128:    
                   1129:     if (defined $Dispatcher{$request_name}) {
                   1130: 	die "Attempting to define a duplicate request handler for $request_name\n";
                   1131:     }
                   1132:     #   Build the client type mask:
                   1133:     
                   1134:     my $client_type_mask = 0;
                   1135:     if($client_ok) {
                   1136: 	$client_type_mask  |= $CLIENT_OK;
                   1137:     }
                   1138:     if($manager_ok) {
                   1139: 	$client_type_mask  |= $MANAGER_OK;
                   1140:     }
                   1141:    
                   1142:     #  Enter the hash:
                   1143:       
                   1144:     my @entry = ($procedure, $must_encode, $client_type_mask);
                   1145:    
                   1146:     $Dispatcher{$request_name} = \@entry;
                   1147:    
                   1148:    
                   1149: }
                   1150: 
                   1151: 
                   1152: #------------------------------------------------------------------
                   1153: 
                   1154: 
                   1155: 
                   1156: 
1.141     foxr     1157: #
1.96      foxr     1158: #  Convert an error return code from lcpasswd to a string value.
                   1159: #
                   1160: sub lcpasswdstrerror {
                   1161:     my $ErrorCode = shift;
1.97      foxr     1162:     if(($ErrorCode < 0) || ($ErrorCode > $lastpwderror)) {
1.96      foxr     1163: 	return "lcpasswd Unrecognized error return value ".$ErrorCode;
                   1164:     } else {
1.98      foxr     1165: 	return $passwderrors[$ErrorCode];
1.96      foxr     1166:     }
                   1167: }
                   1168: 
1.97      foxr     1169: #
                   1170: # Convert an error return code from lcuseradd to a string value:
                   1171: #
                   1172: sub lcuseraddstrerror {
                   1173:     my $ErrorCode = shift;
                   1174:     if(($ErrorCode < 0) || ($ErrorCode > $lastadderror)) {
                   1175: 	return "lcuseradd - Unrecognized error code: ".$ErrorCode;
                   1176:     } else {
1.98      foxr     1177: 	return $adderrors[$ErrorCode];
1.97      foxr     1178:     }
                   1179: }
                   1180: 
1.23      harris41 1181: # grabs exception and records it to log before exiting
                   1182: sub catchexception {
1.27      albertel 1183:     my ($error)=@_;
1.25      www      1184:     $SIG{'QUIT'}='DEFAULT';
                   1185:     $SIG{__DIE__}='DEFAULT';
1.165     albertel 1186:     &status("Catching exception");
1.190     albertel 1187:     &logthis("<font color='red'>CRITICAL: "
1.134     albertel 1188:      ."ABNORMAL EXIT. Child $$ for server $thisserver died through "
1.27      albertel 1189:      ."a crash with this error msg->[$error]</font>");
1.57      www      1190:     &logthis('Famous last words: '.$status.' - '.$lastlog);
1.27      albertel 1191:     if ($client) { print $client "error: $error\n"; }
1.59      www      1192:     $server->close();
1.27      albertel 1193:     die($error);
1.23      harris41 1194: }
                   1195: 
1.63      www      1196: sub timeout {
1.165     albertel 1197:     &status("Handling Timeout");
1.190     albertel 1198:     &logthis("<font color='red'>CRITICAL: TIME OUT ".$$."</font>");
1.63      www      1199:     &catchexception('Timeout');
                   1200: }
1.22      harris41 1201: # -------------------------------- Set signal handlers to record abnormal exits
                   1202: 
                   1203: $SIG{'QUIT'}=\&catchexception;
                   1204: $SIG{__DIE__}=\&catchexception;
                   1205: 
1.81      matthew  1206: # ---------------------------------- Read loncapa_apache.conf and loncapa.conf
1.95      harris41 1207: &status("Read loncapa.conf and loncapa_apache.conf");
                   1208: my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf');
1.141     foxr     1209: %perlvar=%{$perlvarref};
1.80      harris41 1210: undef $perlvarref;
1.19      www      1211: 
1.35      harris41 1212: # ----------------------------- Make sure this process is running from user=www
                   1213: my $wwwid=getpwnam('www');
                   1214: if ($wwwid!=$<) {
1.134     albertel 1215:    my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
                   1216:    my $subj="LON: $currenthostid User ID mismatch";
1.37      harris41 1217:    system("echo 'User ID mismatch.  lond must be run as user www.' |\
1.35      harris41 1218:  mailto $emailto -s '$subj' > /dev/null");
                   1219:    exit 1;
                   1220: }
                   1221: 
1.19      www      1222: # --------------------------------------------- Check if other instance running
                   1223: 
                   1224: my $pidfile="$perlvar{'lonDaemons'}/logs/lond.pid";
                   1225: 
                   1226: if (-e $pidfile) {
                   1227:    my $lfh=IO::File->new("$pidfile");
                   1228:    my $pide=<$lfh>;
                   1229:    chomp($pide);
1.29      harris41 1230:    if (kill 0 => $pide) { die "already running"; }
1.19      www      1231: }
1.1       albertel 1232: 
                   1233: # ------------------------------------------------------------- Read hosts file
                   1234: 
                   1235: 
                   1236: 
                   1237: # establish SERVER socket, bind and listen.
                   1238: $server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'},
                   1239:                                 Type      => SOCK_STREAM,
                   1240:                                 Proto     => 'tcp',
                   1241:                                 Reuse     => 1,
                   1242:                                 Listen    => 10 )
1.29      harris41 1243:   or die "making socket: $@\n";
1.1       albertel 1244: 
                   1245: # --------------------------------------------------------- Do global variables
                   1246: 
                   1247: # global variables
                   1248: 
1.134     albertel 1249: my %children               = ();       # keys are current child process IDs
1.1       albertel 1250: 
                   1251: sub REAPER {                        # takes care of dead children
                   1252:     $SIG{CHLD} = \&REAPER;
1.165     albertel 1253:     &status("Handling child death");
1.178     foxr     1254:     my $pid;
                   1255:     do {
                   1256: 	$pid = waitpid(-1,&WNOHANG());
                   1257: 	if (defined($children{$pid})) {
                   1258: 	    &logthis("Child $pid died");
                   1259: 	    delete($children{$pid});
1.183     albertel 1260: 	} elsif ($pid > 0) {
1.178     foxr     1261: 	    &logthis("Unknown Child $pid died");
                   1262: 	}
                   1263:     } while ( $pid > 0 );
                   1264:     foreach my $child (keys(%children)) {
                   1265: 	$pid = waitpid($child,&WNOHANG());
                   1266: 	if ($pid > 0) {
                   1267: 	    &logthis("Child $child - $pid looks like we missed it's death");
                   1268: 	    delete($children{$pid});
                   1269: 	}
1.176     albertel 1270:     }
1.165     albertel 1271:     &status("Finished Handling child death");
1.1       albertel 1272: }
                   1273: 
                   1274: sub HUNTSMAN {                      # signal handler for SIGINT
1.165     albertel 1275:     &status("Killing children (INT)");
1.1       albertel 1276:     local($SIG{CHLD}) = 'IGNORE';   # we're going to kill our children
                   1277:     kill 'INT' => keys %children;
1.59      www      1278:     &logthis("Free socket: ".shutdown($server,2)); # free up socket
1.1       albertel 1279:     my $execdir=$perlvar{'lonDaemons'};
                   1280:     unlink("$execdir/logs/lond.pid");
1.190     albertel 1281:     &logthis("<font color='red'>CRITICAL: Shutting down</font>");
1.165     albertel 1282:     &status("Done killing children");
1.1       albertel 1283:     exit;                           # clean up with dignity
                   1284: }
                   1285: 
                   1286: sub HUPSMAN {                      # signal handler for SIGHUP
                   1287:     local($SIG{CHLD}) = 'IGNORE';  # we're going to kill our children
1.165     albertel 1288:     &status("Killing children for restart (HUP)");
1.1       albertel 1289:     kill 'INT' => keys %children;
1.59      www      1290:     &logthis("Free socket: ".shutdown($server,2)); # free up socket
1.190     albertel 1291:     &logthis("<font color='red'>CRITICAL: Restarting</font>");
1.134     albertel 1292:     my $execdir=$perlvar{'lonDaemons'};
1.30      harris41 1293:     unlink("$execdir/logs/lond.pid");
1.165     albertel 1294:     &status("Restarting self (HUP)");
1.1       albertel 1295:     exec("$execdir/lond");         # here we go again
                   1296: }
                   1297: 
1.144     foxr     1298: #
1.148     foxr     1299: #    Kill off hashes that describe the host table prior to re-reading it.
                   1300: #    Hashes affected are:
1.200     matthew  1301: #       %hostid, %hostdom %hostip %hostdns.
1.148     foxr     1302: #
                   1303: sub KillHostHashes {
                   1304:     foreach my $key (keys %hostid) {
                   1305: 	delete $hostid{$key};
                   1306:     }
                   1307:     foreach my $key (keys %hostdom) {
                   1308: 	delete $hostdom{$key};
                   1309:     }
                   1310:     foreach my $key (keys %hostip) {
                   1311: 	delete $hostip{$key};
                   1312:     }
1.200     matthew  1313:     foreach my $key (keys %hostdns) {
                   1314: 	delete $hostdns{$key};
                   1315:     }
1.148     foxr     1316: }
                   1317: #
                   1318: #   Read in the host table from file and distribute it into the various hashes:
                   1319: #
                   1320: #    - %hostid  -  Indexed by IP, the loncapa hostname.
                   1321: #    - %hostdom -  Indexed by  loncapa hostname, the domain.
                   1322: #    - %hostip  -  Indexed by hostid, the Ip address of the host.
                   1323: sub ReadHostTable {
                   1324: 
                   1325:     open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
1.200     matthew  1326:     my $myloncapaname = $perlvar{'lonHostID'};
                   1327:     Debug("My loncapa name is : $myloncapaname");
1.148     foxr     1328:     while (my $configline=<CONFIG>) {
1.178     foxr     1329: 	if (!($configline =~ /^\s*\#/)) {
                   1330: 	    my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
                   1331: 	    chomp($ip); $ip=~s/\D+$//;
1.200     matthew  1332: 	    $hostid{$ip}=$id;         # LonCAPA name of host by IP.
                   1333: 	    $hostdom{$id}=$domain;    # LonCAPA domain name of host. 
                   1334: 	    $hostip{$id}=$ip;	      # IP address of host.
                   1335: 	    $hostdns{$name} = $id;    # LonCAPA name of host by DNS.
                   1336: 
                   1337: 	    if ($id eq $perlvar{'lonHostID'}) { 
                   1338: 		Debug("Found me in the host table: $name");
                   1339: 		$thisserver=$name; 
                   1340: 	    }
1.178     foxr     1341: 	}
1.148     foxr     1342:     }
                   1343:     close(CONFIG);
                   1344: }
                   1345: #
                   1346: #  Reload the Apache daemon's state.
1.150     foxr     1347: #  This is done by invoking /home/httpd/perl/apachereload
                   1348: #  a setuid perl script that can be root for us to do this job.
1.148     foxr     1349: #
                   1350: sub ReloadApache {
1.150     foxr     1351:     my $execdir = $perlvar{'lonDaemons'};
                   1352:     my $script  = $execdir."/apachereload";
                   1353:     system($script);
1.148     foxr     1354: }
                   1355: 
                   1356: #
1.144     foxr     1357: #   Called in response to a USR2 signal.
                   1358: #   - Reread hosts.tab
                   1359: #   - All children connected to hosts that were removed from hosts.tab
                   1360: #     are killed via SIGINT
                   1361: #   - All children connected to previously existing hosts are sent SIGUSR1
                   1362: #   - Our internal hosts hash is updated to reflect the new contents of
                   1363: #     hosts.tab causing connections from hosts added to hosts.tab to
                   1364: #     now be honored.
                   1365: #
                   1366: sub UpdateHosts {
1.165     albertel 1367:     &status("Reload hosts.tab");
1.147     foxr     1368:     logthis('<font color="blue"> Updating connections </font>');
1.148     foxr     1369:     #
                   1370:     #  The %children hash has the set of IP's we currently have children
                   1371:     #  on.  These need to be matched against records in the hosts.tab
                   1372:     #  Any ip's no longer in the table get killed off they correspond to
                   1373:     #  either dropped or changed hosts.  Note that the re-read of the table
                   1374:     #  will take care of new and changed hosts as connections come into being.
                   1375: 
                   1376: 
                   1377:     KillHostHashes;
                   1378:     ReadHostTable;
                   1379: 
                   1380:     foreach my $child (keys %children) {
                   1381: 	my $childip = $children{$child};
                   1382: 	if(!$hostid{$childip}) {
1.149     foxr     1383: 	    logthis('<font color="blue"> UpdateHosts killing child '
                   1384: 		    ." $child for ip $childip </font>");
1.148     foxr     1385: 	    kill('INT', $child);
1.149     foxr     1386: 	} else {
                   1387: 	    logthis('<font color="green"> keeping child for ip '
                   1388: 		    ." $childip (pid=$child) </font>");
1.148     foxr     1389: 	}
                   1390:     }
                   1391:     ReloadApache;
1.165     albertel 1392:     &status("Finished reloading hosts.tab");
1.144     foxr     1393: }
                   1394: 
1.148     foxr     1395: 
1.57      www      1396: sub checkchildren {
1.165     albertel 1397:     &status("Checking on the children (sending signals)");
1.57      www      1398:     &initnewstatus();
                   1399:     &logstatus();
                   1400:     &logthis('Going to check on the children');
1.134     albertel 1401:     my $docdir=$perlvar{'lonDocRoot'};
1.61      harris41 1402:     foreach (sort keys %children) {
1.57      www      1403: 	sleep 1;
                   1404:         unless (kill 'USR1' => $_) {
                   1405: 	    &logthis ('Child '.$_.' is dead');
                   1406:             &logstatus($$.' is dead');
                   1407:         } 
1.61      harris41 1408:     }
1.63      www      1409:     sleep 5;
1.113     albertel 1410:     $SIG{ALRM} = sub { die "timeout" };
                   1411:     $SIG{__DIE__} = 'DEFAULT';
1.165     albertel 1412:     &status("Checking on the children (waiting for reports)");
1.63      www      1413:     foreach (sort keys %children) {
                   1414:         unless (-e "$docdir/lon-status/londchld/$_.txt") {
1.113     albertel 1415:           eval {
                   1416:             alarm(300);
1.63      www      1417: 	    &logthis('Child '.$_.' did not respond');
1.67      albertel 1418: 	    kill 9 => $_;
1.131     albertel 1419: 	    #$emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
                   1420: 	    #$subj="LON: $currenthostid killed lond process $_";
                   1421: 	    #my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`;
                   1422: 	    #$execdir=$perlvar{'lonDaemons'};
                   1423: 	    #$result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`;
1.113     albertel 1424: 	    alarm(0);
                   1425: 	  }
1.63      www      1426:         }
                   1427:     }
1.113     albertel 1428:     $SIG{ALRM} = 'DEFAULT';
1.155     albertel 1429:     $SIG{__DIE__} = \&catchexception;
1.165     albertel 1430:     &status("Finished checking children");
1.57      www      1431: }
                   1432: 
1.1       albertel 1433: # --------------------------------------------------------------------- Logging
                   1434: 
                   1435: sub logthis {
                   1436:     my $message=shift;
                   1437:     my $execdir=$perlvar{'lonDaemons'};
                   1438:     my $fh=IO::File->new(">>$execdir/logs/lond.log");
                   1439:     my $now=time;
                   1440:     my $local=localtime($now);
1.58      www      1441:     $lastlog=$local.': '.$message;
1.1       albertel 1442:     print $fh "$local ($$): $message\n";
                   1443: }
                   1444: 
1.77      foxr     1445: # ------------------------- Conditional log if $DEBUG true.
                   1446: sub Debug {
                   1447:     my $message = shift;
                   1448:     if($DEBUG) {
                   1449: 	&logthis($message);
                   1450:     }
                   1451: }
1.161     foxr     1452: 
                   1453: #
                   1454: #   Sub to do replies to client.. this gives a hook for some
                   1455: #   debug tracing too:
                   1456: #  Parameters:
                   1457: #     fd      - File open on client.
                   1458: #     reply   - Text to send to client.
                   1459: #     request - Original request from client.
                   1460: #
                   1461: sub Reply {
1.192     foxr     1462: 
                   1463:     my ($fd, $reply, $request) = @_;
1.161     foxr     1464: 
                   1465:     print $fd $reply;
                   1466:     Debug("Request was $request  Reply was $reply");
                   1467: 
                   1468: }
1.57      www      1469: # ------------------------------------------------------------------ Log status
                   1470: 
                   1471: sub logstatus {
1.178     foxr     1472:     &status("Doing logging");
                   1473:     my $docdir=$perlvar{'lonDocRoot'};
                   1474:     {
                   1475:     my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt");
1.200     matthew  1476:     print $fh $$."\t".$clientname."\t".$currenthostid."\t"
                   1477: 	.$status."\t".$lastlog."\t $keymode\n";
1.178     foxr     1478:     $fh->close();
                   1479:     }
                   1480:     &status("Finished londstatus.txt");
                   1481:     {
                   1482: 	my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt");
1.200     matthew  1483:         print $fh $status."\n".$lastlog."\n".time."\n$keymode";
1.178     foxr     1484:         $fh->close();
                   1485:     }
                   1486:     &status("Finished logging");
1.57      www      1487: }
                   1488: 
                   1489: sub initnewstatus {
                   1490:     my $docdir=$perlvar{'lonDocRoot'};
                   1491:     my $fh=IO::File->new(">$docdir/lon-status/londstatus.txt");
                   1492:     my $now=time;
                   1493:     my $local=localtime($now);
                   1494:     print $fh "LOND status $local - parent $$\n\n";
1.64      www      1495:     opendir(DIR,"$docdir/lon-status/londchld");
1.134     albertel 1496:     while (my $filename=readdir(DIR)) {
1.64      www      1497:         unlink("$docdir/lon-status/londchld/$filename");
                   1498:     }
                   1499:     closedir(DIR);
1.57      www      1500: }
                   1501: 
                   1502: # -------------------------------------------------------------- Status setting
                   1503: 
                   1504: sub status {
                   1505:     my $what=shift;
                   1506:     my $now=time;
                   1507:     my $local=localtime($now);
1.178     foxr     1508:     $status=$local.': '.$what;
                   1509:     $0='lond: '.$what.' '.$local;
1.57      www      1510: }
1.11      www      1511: 
                   1512: # -------------------------------------------------------- Escape Special Chars
                   1513: 
                   1514: sub escape {
                   1515:     my $str=shift;
                   1516:     $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
                   1517:     return $str;
                   1518: }
                   1519: 
                   1520: # ----------------------------------------------------- Un-Escape Special Chars
                   1521: 
                   1522: sub unescape {
                   1523:     my $str=shift;
                   1524:     $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
                   1525:     return $str;
                   1526: }
                   1527: 
1.1       albertel 1528: # ----------------------------------------------------------- Send USR1 to lonc
                   1529: 
                   1530: sub reconlonc {
                   1531:     my $peerfile=shift;
                   1532:     &logthis("Trying to reconnect for $peerfile");
                   1533:     my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
                   1534:     if (my $fh=IO::File->new("$loncfile")) {
                   1535: 	my $loncpid=<$fh>;
                   1536:         chomp($loncpid);
                   1537:         if (kill 0 => $loncpid) {
                   1538: 	    &logthis("lonc at pid $loncpid responding, sending USR1");
                   1539:             kill USR1 => $loncpid;
                   1540:         } else {
1.9       www      1541: 	    &logthis(
1.190     albertel 1542:               "<font color='red'>CRITICAL: "
1.9       www      1543:              ."lonc at pid $loncpid not responding, giving up</font>");
1.1       albertel 1544:         }
                   1545:     } else {
1.190     albertel 1546:       &logthis('<font color="red">CRITICAL: lonc not running, giving up</font>');
1.1       albertel 1547:     }
                   1548: }
                   1549: 
                   1550: # -------------------------------------------------- Non-critical communication
1.11      www      1551: 
1.1       albertel 1552: sub subreply {
                   1553:     my ($cmd,$server)=@_;
                   1554:     my $peerfile="$perlvar{'lonSockDir'}/$server";
                   1555:     my $sclient=IO::Socket::UNIX->new(Peer    =>"$peerfile",
                   1556:                                       Type    => SOCK_STREAM,
                   1557:                                       Timeout => 10)
                   1558:        or return "con_lost";
                   1559:     print $sclient "$cmd\n";
                   1560:     my $answer=<$sclient>;
                   1561:     chomp($answer);
                   1562:     if (!$answer) { $answer="con_lost"; }
                   1563:     return $answer;
                   1564: }
                   1565: 
                   1566: sub reply {
                   1567:   my ($cmd,$server)=@_;
                   1568:   my $answer;
1.115     albertel 1569:   if ($server ne $currenthostid) { 
1.1       albertel 1570:     $answer=subreply($cmd,$server);
                   1571:     if ($answer eq 'con_lost') {
                   1572: 	$answer=subreply("ping",$server);
                   1573:         if ($answer ne $server) {
1.115     albertel 1574: 	    &logthis("sub reply: answer != server answer is $answer, server is $server");
1.1       albertel 1575:            &reconlonc("$perlvar{'lonSockDir'}/$server");
                   1576:         }
                   1577:         $answer=subreply($cmd,$server);
                   1578:     }
                   1579:   } else {
                   1580:     $answer='self_reply';
                   1581:   } 
                   1582:   return $answer;
                   1583: }
                   1584: 
1.13      www      1585: # -------------------------------------------------------------- Talk to lonsql
                   1586: 
1.12      harris41 1587: sub sqlreply {
                   1588:     my ($cmd)=@_;
                   1589:     my $answer=subsqlreply($cmd);
                   1590:     if ($answer eq 'con_lost') { $answer=subsqlreply($cmd); }
                   1591:     return $answer;
                   1592: }
                   1593: 
                   1594: sub subsqlreply {
                   1595:     my ($cmd)=@_;
                   1596:     my $unixsock="mysqlsock";
                   1597:     my $peerfile="$perlvar{'lonSockDir'}/$unixsock";
                   1598:     my $sclient=IO::Socket::UNIX->new(Peer    =>"$peerfile",
                   1599:                                       Type    => SOCK_STREAM,
                   1600:                                       Timeout => 10)
                   1601:        or return "con_lost";
                   1602:     print $sclient "$cmd\n";
                   1603:     my $answer=<$sclient>;
                   1604:     chomp($answer);
                   1605:     if (!$answer) { $answer="con_lost"; }
                   1606:     return $answer;
                   1607: }
                   1608: 
1.1       albertel 1609: # -------------------------------------------- Return path to profile directory
1.11      www      1610: 
1.1       albertel 1611: sub propath {
                   1612:     my ($udom,$uname)=@_;
                   1613:     $udom=~s/\W//g;
                   1614:     $uname=~s/\W//g;
1.16      www      1615:     my $subdir=$uname.'__';
1.1       albertel 1616:     $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
                   1617:     my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
                   1618:     return $proname;
                   1619: } 
                   1620: 
                   1621: # --------------------------------------- Is this the home server of an author?
1.11      www      1622: 
1.1       albertel 1623: sub ishome {
                   1624:     my $author=shift;
                   1625:     $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
                   1626:     my ($udom,$uname)=split(/\//,$author);
                   1627:     my $proname=propath($udom,$uname);
                   1628:     if (-e $proname) {
                   1629: 	return 'owner';
                   1630:     } else {
                   1631:         return 'not_owner';
                   1632:     }
                   1633: }
                   1634: 
                   1635: # ======================================================= Continue main program
                   1636: # ---------------------------------------------------- Fork once and dissociate
                   1637: 
1.134     albertel 1638: my $fpid=fork;
1.1       albertel 1639: exit if $fpid;
1.29      harris41 1640: die "Couldn't fork: $!" unless defined ($fpid);
1.1       albertel 1641: 
1.29      harris41 1642: POSIX::setsid() or die "Can't start new session: $!";
1.1       albertel 1643: 
                   1644: # ------------------------------------------------------- Write our PID on disk
                   1645: 
1.134     albertel 1646: my $execdir=$perlvar{'lonDaemons'};
1.1       albertel 1647: open (PIDSAVE,">$execdir/logs/lond.pid");
                   1648: print PIDSAVE "$$\n";
                   1649: close(PIDSAVE);
1.190     albertel 1650: &logthis("<font color='red'>CRITICAL: ---------- Starting ----------</font>");
1.57      www      1651: &status('Starting');
1.1       albertel 1652: 
1.106     foxr     1653: 
1.1       albertel 1654: 
                   1655: # ----------------------------------------------------- Install signal handlers
                   1656: 
1.57      www      1657: 
1.1       albertel 1658: $SIG{CHLD} = \&REAPER;
                   1659: $SIG{INT}  = $SIG{TERM} = \&HUNTSMAN;
                   1660: $SIG{HUP}  = \&HUPSMAN;
1.57      www      1661: $SIG{USR1} = \&checkchildren;
1.144     foxr     1662: $SIG{USR2} = \&UpdateHosts;
1.106     foxr     1663: 
1.148     foxr     1664: #  Read the host hashes:
                   1665: 
                   1666: ReadHostTable;
1.106     foxr     1667: 
                   1668: # --------------------------------------------------------------
                   1669: #   Accept connections.  When a connection comes in, it is validated
                   1670: #   and if good, a child process is created to process transactions
                   1671: #   along the connection.
                   1672: 
1.1       albertel 1673: while (1) {
1.165     albertel 1674:     &status('Starting accept');
1.106     foxr     1675:     $client = $server->accept() or next;
1.165     albertel 1676:     &status('Accepted '.$client.' off to spawn');
1.106     foxr     1677:     make_new_child($client);
1.165     albertel 1678:     &status('Finished spawning');
1.1       albertel 1679: }
                   1680: 
                   1681: sub make_new_child {
                   1682:     my $pid;
1.207     foxr     1683: #    my $cipher;     # Now global
1.1       albertel 1684:     my $sigset;
1.106     foxr     1685: 
                   1686:     $client = shift;
1.165     albertel 1687:     &status('Starting new child '.$client);
1.161     foxr     1688:     &logthis('<font color="green"> Attempting to start child ('.$client.
                   1689: 	     ")</font>");    
1.1       albertel 1690:     # block signal for fork
                   1691:     $sigset = POSIX::SigSet->new(SIGINT);
                   1692:     sigprocmask(SIG_BLOCK, $sigset)
1.29      harris41 1693:         or die "Can't block SIGINT for fork: $!\n";
1.134     albertel 1694: 
1.29      harris41 1695:     die "fork: $!" unless defined ($pid = fork);
1.148     foxr     1696: 
                   1697:     $client->sockopt(SO_KEEPALIVE, 1); # Enable monitoring of
                   1698: 	                               # connection liveness.
                   1699: 
                   1700:     #
                   1701:     #  Figure out who we're talking to so we can record the peer in 
                   1702:     #  the pid hash.
                   1703:     #
                   1704:     my $caller = getpeername($client);
1.180     albertel 1705:     my ($port,$iaddr);
                   1706:     if (defined($caller) && length($caller) > 0) {
                   1707: 	($port,$iaddr)=unpack_sockaddr_in($caller);
                   1708:     } else {
                   1709: 	&logthis("Unable to determine who caller was, getpeername returned nothing");
                   1710:     }
                   1711:     if (defined($iaddr)) {
1.200     matthew  1712: 	$clientip  = inet_ntoa($iaddr);
                   1713: 	Debug("Connected with $clientip");
                   1714: 	$clientdns = gethostbyaddr($iaddr, AF_INET);
                   1715: 	Debug("Connected with $clientdns by name");
1.180     albertel 1716:     } else {
1.200     matthew  1717: 	&logthis("Unable to determine clientip");
1.180     albertel 1718: 	$clientip='Unavailable';
                   1719:     }
1.1       albertel 1720:     
                   1721:     if ($pid) {
                   1722:         # Parent records the child's birth and returns.
                   1723:         sigprocmask(SIG_UNBLOCK, $sigset)
1.29      harris41 1724:             or die "Can't unblock SIGINT for fork: $!\n";
1.148     foxr     1725:         $children{$pid} = $clientip;
1.57      www      1726:         &status('Started child '.$pid);
1.1       albertel 1727:         return;
                   1728:     } else {
                   1729:         # Child can *not* return from this subroutine.
                   1730:         $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before
1.126     albertel 1731:         $SIG{CHLD} = 'DEFAULT'; #make this default so that pwauth returns 
                   1732:                                 #don't get intercepted
1.57      www      1733:         $SIG{USR1}= \&logstatus;
1.63      www      1734:         $SIG{ALRM}= \&timeout;
1.57      www      1735:         $lastlog='Forked ';
                   1736:         $status='Forked';
                   1737: 
1.1       albertel 1738:         # unblock signals
                   1739:         sigprocmask(SIG_UNBLOCK, $sigset)
1.29      harris41 1740:             or die "Can't unblock SIGINT for fork: $!\n";
1.13      www      1741: 
1.207     foxr     1742: #        my $tmpsnum=0;            # Now global
1.178     foxr     1743: #---------------------------------------------------- kerberos 5 initialization
1.91      albertel 1744:         &Authen::Krb5::init_context();
                   1745:         &Authen::Krb5::init_ets();
                   1746: 
1.161     foxr     1747: 	&status('Accepted connection');
1.1       albertel 1748: # =============================================================================
                   1749:             # do something with the connection
                   1750: # -----------------------------------------------------------------------------
1.200     matthew  1751: 	# see if we know client and 'check' for spoof IP by ineffective challenge
1.148     foxr     1752: 
1.161     foxr     1753: 	ReadManagerTable;	# May also be a manager!!
                   1754: 	
                   1755: 	my $clientrec=($hostid{$clientip}     ne undef);
                   1756: 	my $ismanager=($managers{$clientip}    ne undef);
                   1757: 	$clientname  = "[unknonwn]";
                   1758: 	if($clientrec) {	# Establish client type.
                   1759: 	    $ConnectionType = "client";
                   1760: 	    $clientname = $hostid{$clientip};
                   1761: 	    if($ismanager) {
                   1762: 		$ConnectionType = "both";
                   1763: 	    }
                   1764: 	} else {
                   1765: 	    $ConnectionType = "manager";
                   1766: 	    $clientname = $managers{$clientip};
                   1767: 	}
                   1768: 	my $clientok;
1.200     matthew  1769: 
1.161     foxr     1770: 	if ($clientrec || $ismanager) {
                   1771: 	    &status("Waiting for init from $clientip $clientname");
                   1772: 	    &logthis('<font color="yellow">INFO: Connection, '.
                   1773: 		     $clientip.
                   1774: 		  " ($clientname) connection type = $ConnectionType </font>" );
                   1775: 	    &status("Connecting $clientip  ($clientname))"); 
                   1776: 	    my $remotereq=<$client>;
1.200     matthew  1777: 	    chomp($remotereq);
                   1778: 	    Debug("Got init: $remotereq");
                   1779: 	    my $inikeyword = split(/:/, $remotereq);
1.161     foxr     1780: 	    if ($remotereq =~ /^init/) {
                   1781: 		&sethost("sethost:$perlvar{'lonHostID'}");
1.200     matthew  1782: 		#
                   1783: 		#  If the remote is attempting a local init... give that a try:
                   1784: 		#
                   1785: 		my ($i, $inittype) = split(/:/, $remotereq);
                   1786: 
                   1787: 		# If the connection type is ssl, but I didn't get my
                   1788: 		# certificate files yet, then I'll drop  back to 
                   1789: 		# insecure (if allowed).
                   1790: 		
                   1791: 		if($inittype eq "ssl") {
                   1792: 		    my ($ca, $cert) = lonssl::CertificateFile;
                   1793: 		    my $kfile       = lonssl::KeyFile;
                   1794: 		    if((!$ca)   || 
                   1795: 		       (!$cert) || 
                   1796: 		       (!$kfile)) {
                   1797: 			$inittype = ""; # This forces insecure attempt.
                   1798: 			&logthis("<font color=\"blue\"> Certificates not "
                   1799: 				 ."installed -- trying insecure auth</font>");
                   1800: 		    }
                   1801: 		    else {	# SSL certificates are in place so
                   1802: 		    }		# Leave the inittype alone.
                   1803: 		}
                   1804: 
                   1805: 		if($inittype eq "local") {
                   1806: 		    my $key = LocalConnection($client, $remotereq);
                   1807: 		    if($key) {
                   1808: 			Debug("Got local key $key");
                   1809: 			$clientok     = 1;
                   1810: 			my $cipherkey = pack("H32", $key);
                   1811: 			$cipher       = new IDEA($cipherkey);
                   1812: 			print $client "ok:local\n";
                   1813: 			&logthis('<font color="green"'
                   1814: 				 . "Successful local authentication </font>");
                   1815: 			$keymode = "local"
                   1816: 		    } else {
                   1817: 			Debug("Failed to get local key");
                   1818: 			$clientok = 0;
                   1819: 			shutdown($client, 3);
                   1820: 			close $client;
                   1821: 		    }
                   1822: 		} elsif ($inittype eq "ssl") {
                   1823: 		    my $key = SSLConnection($client);
                   1824: 		    if ($key) {
                   1825: 			$clientok = 1;
                   1826: 			my $cipherkey = pack("H32", $key);
                   1827: 			$cipher       = new IDEA($cipherkey);
                   1828: 			&logthis('<font color="green">'
                   1829: 				 ."Successfull ssl authentication with $clientname </font>");
                   1830: 			$keymode = "ssl";
                   1831: 	     
                   1832: 		    } else {
                   1833: 			$clientok = 0;
                   1834: 			close $client;
                   1835: 		    }
                   1836: 	   
1.161     foxr     1837: 		} else {
1.200     matthew  1838: 		    my $ok = InsecureConnection($client);
                   1839: 		    if($ok) {
                   1840: 			$clientok = 1;
                   1841: 			&logthis('<font color="green">'
                   1842: 				 ."Successful insecure authentication with $clientname </font>");
                   1843: 			print $client "ok\n";
                   1844: 			$keymode = "insecure";
                   1845: 		    } else {
                   1846: 			&logthis('<font color="yellow">'
                   1847: 				  ."Attempted insecure connection disallowed </font>");
                   1848: 			close $client;
                   1849: 			$clientok = 0;
                   1850: 			
                   1851: 		    }
1.161     foxr     1852: 		}
1.2       www      1853: 	    } else {
1.161     foxr     1854: 		&logthis(
1.190     albertel 1855: 			 "<font color='blue'>WARNING: "
1.161     foxr     1856: 			 ."$clientip failed to initialize: >$remotereq< </font>");
                   1857: 		&status('No init '.$clientip);
                   1858: 	    }
1.200     matthew  1859: 	    
1.161     foxr     1860: 	} else {
                   1861: 	    &logthis(
1.190     albertel 1862: 		     "<font color='blue'>WARNING: Unknown client $clientip</font>");
1.161     foxr     1863: 	    &status('Hung up on '.$clientip);
                   1864: 	}
1.200     matthew  1865:  
1.161     foxr     1866: 	if ($clientok) {
1.1       albertel 1867: # ---------------- New known client connecting, could mean machine online again
1.161     foxr     1868: 	    
                   1869: 	    foreach my $id (keys(%hostip)) {
                   1870: 		if ($hostip{$id} ne $clientip ||
                   1871: 		    $hostip{$currenthostid} eq $clientip) {
                   1872: 		    # no need to try to do recon's to myself
                   1873: 		    next;
1.115     albertel 1874: 		}
1.161     foxr     1875: 		&reconlonc("$perlvar{'lonSockDir'}/$id");
                   1876: 	    }
1.190     albertel 1877: 	    &logthis("<font color='green'>Established connection: $clientname</font>");
1.161     foxr     1878: 	    &status('Will listen to '.$clientname);
1.178     foxr     1879: # ------------------------------------------------------------ Process requests
                   1880: 	    while (my $userinput=<$client>) {
                   1881:                 chomp($userinput);
                   1882: 		Debug("Request = $userinput\n");
                   1883:                 &status('Processing '.$clientname.': '.$userinput);
                   1884:                 my $wasenc=0;
                   1885:                 alarm(120);
                   1886: # ------------------------------------------------------------ See if encrypted
                   1887: 		if ($userinput =~ /^enc/) {
                   1888: 		    if ($cipher) {
                   1889: 			my ($cmd,$cmdlength,$encinput)=split(/:/,$userinput);
                   1890: 			$userinput='';
                   1891: 			for (my $encidx=0;$encidx<length($encinput);$encidx+=16) {
                   1892: 			    $userinput.=
                   1893: 				$cipher->decrypt(
                   1894: 						 pack("H16",substr($encinput,$encidx,16))
                   1895: 						 );
                   1896: 			}
                   1897: 			$userinput=substr($userinput,0,$cmdlength);
                   1898: 			$wasenc=1;
                   1899: 		    }
                   1900: 		}
                   1901: 		
                   1902: # ------------------------------------------------------------- Normal commands
                   1903: # ------------------------------------------------------------------------ ping
                   1904: 		if ($userinput =~ /^ping/) {	# client only
                   1905: 		    if(isClient) {
                   1906: 			print $client "$currenthostid\n";
                   1907: 		    } else {
                   1908: 			Reply($client, "refused\n", $userinput);
                   1909: 		    }
                   1910: # ------------------------------------------------------------------------ pong
                   1911: 		}elsif ($userinput =~ /^pong/) { # client only
                   1912: 		    if(isClient) {
                   1913: 			my $reply=&reply("ping",$clientname);
                   1914: 			print $client "$currenthostid:$reply\n"; 
                   1915: 		    } else {
                   1916: 			Reply($client, "refused\n", $userinput);
                   1917: 		    }
                   1918: # ------------------------------------------------------------------------ ekey
                   1919: 		} elsif ($userinput =~ /^ekey/) { # ok for both clients & mgrs
                   1920: 		    my $buildkey=time.$$.int(rand 100000);
                   1921: 		    $buildkey=~tr/1-6/A-F/;
                   1922: 		    $buildkey=int(rand 100000).$buildkey.int(rand 100000);
                   1923: 		    my $key=$currenthostid.$clientname;
                   1924: 		    $key=~tr/a-z/A-Z/;
                   1925: 		    $key=~tr/G-P/0-9/;
                   1926: 		    $key=~tr/Q-Z/0-9/;
                   1927: 		    $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
                   1928: 		    $key=substr($key,0,32);
                   1929: 		    my $cipherkey=pack("H32",$key);
                   1930: 		    $cipher=new IDEA $cipherkey;
                   1931: 		    print $client "$buildkey\n"; 
                   1932: # ------------------------------------------------------------------------ load
                   1933: 		} elsif ($userinput =~ /^load/) { # client only
                   1934: 		    if (isClient) {
                   1935: 			my $loadavg;
                   1936: 			{
                   1937: 			    my $loadfile=IO::File->new('/proc/loadavg');
                   1938: 			    $loadavg=<$loadfile>;
                   1939: 			}
                   1940: 			$loadavg =~ s/\s.*//g;
                   1941: 			my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
                   1942: 			print $client "$loadpercent\n";
                   1943: 		    } else {
                   1944: 			Reply($client, "refused\n", $userinput);
                   1945: 	       
                   1946: 		    }
                   1947: # -------------------------------------------------------------------- userload
                   1948: 		} elsif ($userinput =~ /^userload/) { # client only
                   1949: 		    if(isClient) {
                   1950: 			my $userloadpercent=&userload();
                   1951: 			print $client "$userloadpercent\n";
                   1952: 		    } else {
                   1953: 			Reply($client, "refused\n", $userinput);
                   1954: 		     
                   1955: 		    }
                   1956: #
                   1957: #        Transactions requiring encryption:
                   1958: #
                   1959: # ----------------------------------------------------------------- currentauth
                   1960: 		} elsif ($userinput =~ /^currentauth/) {
                   1961: 		    if (($wasenc==1)  && isClient) { # Encoded & client only.
                   1962: 			my ($cmd,$udom,$uname)=split(/:/,$userinput);
                   1963: 			my $result = GetAuthType($udom, $uname);
                   1964: 			if($result eq "nouser") {
                   1965: 			    print $client "unknown_user\n";
                   1966: 			}
                   1967: 			else {
                   1968: 			    print $client "$result\n"
                   1969: 			    }
                   1970: 		    } else {
                   1971: 			Reply($client, "refused\n", $userinput);
                   1972: 			
                   1973: 		    }
                   1974: #--------------------------------------------------------------------- pushfile
                   1975: 		} elsif($userinput =~ /^pushfile/) {	# encoded & manager.
                   1976: 		    if(($wasenc == 1) && isManager) {
                   1977: 			my $cert = GetCertificate($userinput);
                   1978: 			if(ValidManager($cert)) {
                   1979: 			    my $reply = PushFile($userinput);
                   1980: 			    print $client "$reply\n";
                   1981: 			} else {
                   1982: 			    print $client "refused\n";
                   1983: 			} 
                   1984: 		    } else {
                   1985: 			Reply($client, "refused\n", $userinput);
                   1986: 			
                   1987: 		    }
                   1988: #--------------------------------------------------------------------- reinit
                   1989: 		} elsif($userinput =~ /^reinit/) { # Encoded and manager
                   1990: 			if (($wasenc == 1) && isManager) {
                   1991: 				my $cert = GetCertificate($userinput);
                   1992: 				if(ValidManager($cert)) {
                   1993: 					chomp($userinput);
                   1994: 					my $reply = ReinitProcess($userinput);
                   1995: 					print $client  "$reply\n";
                   1996: 				} else {
                   1997: 					 print $client "refused\n";
                   1998: 				}
                   1999: 			} else {
                   2000: 				Reply($client, "refused\n", $userinput);
                   2001: 			}
                   2002: #------------------------------------------------------------------------- edit
                   2003: 		    } elsif ($userinput =~ /^edit/) {    # encoded and manager:
                   2004: 			if(($wasenc ==1) && (isManager)) {
                   2005: 			    my $cert = GetCertificate($userinput);
                   2006: 			    if(ValidManager($cert)) {
                   2007:                my($command, $filetype, $script) = split(/:/, $userinput);
                   2008:                if (($filetype eq "hosts") || ($filetype eq "domain")) {
                   2009:                   if($script ne "") {
                   2010: 		      Reply($client, EditFile($userinput));
                   2011:                   } else {
                   2012:                      Reply($client,"refused\n",$userinput);
                   2013:                   }
                   2014:                } else {
                   2015:                   Reply($client,"refused\n",$userinput);
                   2016:                }
                   2017:             } else {
                   2018:                Reply($client,"refused\n",$userinput);
                   2019:             }
                   2020:          } else {
                   2021: 	     Reply($client,"refused\n",$userinput);
                   2022: 	 }
                   2023: # ------------------------------------------------------------------------ auth
                   2024: 		    } elsif ($userinput =~ /^auth/) { # Encoded and client only.
                   2025: 		    if (($wasenc==1) && isClient) {
                   2026: 			my ($cmd,$udom,$uname,$upass)=split(/:/,$userinput);
                   2027: 			chomp($upass);
                   2028: 			$upass=unescape($upass);
                   2029: 			my $proname=propath($udom,$uname);
                   2030: 			my $passfilename="$proname/passwd";
                   2031: 			if (-e $passfilename) {
                   2032: 			    my $pf = IO::File->new($passfilename);
                   2033: 			    my $realpasswd=<$pf>;
                   2034: 			    chomp($realpasswd);
                   2035: 			    my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
                   2036: 			    my $pwdcorrect=0;
                   2037: 			    if ($howpwd eq 'internal') {
                   2038: 				&Debug("Internal auth");
                   2039: 				$pwdcorrect=
                   2040: 				    (crypt($upass,$contentpwd) eq $contentpwd);
                   2041: 			    } elsif ($howpwd eq 'unix') {
                   2042: 				&Debug("Unix auth");
                   2043: 				if((getpwnam($uname))[1] eq "") { #no such user!
                   2044: 				    $pwdcorrect = 0;
                   2045: 				} else {
                   2046: 				    $contentpwd=(getpwnam($uname))[1];
                   2047: 				    my $pwauth_path="/usr/local/sbin/pwauth";
                   2048: 				    unless ($contentpwd eq 'x') {
                   2049: 					$pwdcorrect=
                   2050: 					    (crypt($upass,$contentpwd) eq 
                   2051: 					     $contentpwd);
                   2052: 				    }
                   2053: 				    
                   2054: 				    elsif (-e $pwauth_path) {
                   2055: 					open PWAUTH, "|$pwauth_path" or
                   2056: 					    die "Cannot invoke authentication";
                   2057: 					print PWAUTH "$uname\n$upass\n";
                   2058: 					close PWAUTH;
                   2059: 					$pwdcorrect=!$?;
                   2060: 				    }
                   2061: 				}
                   2062: 			    } elsif ($howpwd eq 'krb4') {
                   2063: 				my $null=pack("C",0);
                   2064: 				unless ($upass=~/$null/) {
                   2065: 				    my $krb4_error = &Authen::Krb4::get_pw_in_tkt
                   2066: 					($uname,"",$contentpwd,'krbtgt',
                   2067: 					 $contentpwd,1,$upass);
                   2068: 				    if (!$krb4_error) {
                   2069: 					$pwdcorrect = 1;
                   2070: 				    } else { 
                   2071: 					$pwdcorrect=0; 
                   2072: 					# log error if it is not a bad password
                   2073: 					if ($krb4_error != 62) {
1.191     albertel 2074: 					    &logthis('krb4:'.$uname.','.
1.178     foxr     2075: 						     &Authen::Krb4::get_err_txt($Authen::Krb4::error));
                   2076: 					}
                   2077: 				    }
                   2078: 				}
                   2079: 			    } elsif ($howpwd eq 'krb5') {
                   2080: 				my $null=pack("C",0);
                   2081: 				unless ($upass=~/$null/) {
                   2082: 				    my $krbclient=&Authen::Krb5::parse_name($uname.'@'.$contentpwd);
                   2083: 				    my $krbservice="krbtgt/".$contentpwd."\@".$contentpwd;
                   2084: 				    my $krbserver=&Authen::Krb5::parse_name($krbservice);
                   2085: 				    my $credentials=&Authen::Krb5::cc_default();
                   2086: 				    $credentials->initialize($krbclient);
                   2087: 				    my $krbreturn = 
                   2088: 					&Authen::Krb5::get_in_tkt_with_password(
                   2089: 										$krbclient,$krbserver,$upass,$credentials);
                   2090: #				  unless ($krbreturn) {
                   2091: #				      &logthis("Krb5 Error: ".
                   2092: #					       &Authen::Krb5::error());
                   2093: #				  }
                   2094: 				    $pwdcorrect = ($krbreturn == 1);
                   2095: 				} else { $pwdcorrect=0; }
                   2096: 			    } elsif ($howpwd eq 'localauth') {
                   2097: 				$pwdcorrect=&localauth::localauth($uname,$upass,
                   2098: 								  $contentpwd);
                   2099: 			    }
                   2100: 			    if ($pwdcorrect) {
                   2101: 				print $client "authorized\n";
                   2102: 			    } else {
                   2103: 				print $client "non_authorized\n";
                   2104: 			    }  
                   2105: 			} else {
                   2106: 			    print $client "unknown_user\n";
                   2107: 			}
                   2108: 		    } else {
                   2109: 			Reply($client, "refused\n", $userinput);
                   2110: 		       
                   2111: 		    }
                   2112: # ---------------------------------------------------------------------- passwd
                   2113: 		} elsif ($userinput =~ /^passwd/) { # encoded and client
                   2114: 		    if (($wasenc==1) && isClient) {
                   2115: 			my 
                   2116: 			    ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);
                   2117: 			chomp($npass);
                   2118: 			$upass=&unescape($upass);
                   2119: 			$npass=&unescape($npass);
                   2120: 			&Debug("Trying to change password for $uname");
                   2121: 			my $proname=propath($udom,$uname);
                   2122: 			my $passfilename="$proname/passwd";
                   2123: 			if (-e $passfilename) {
                   2124: 			    my $realpasswd;
                   2125: 			    { my $pf = IO::File->new($passfilename);
                   2126: 			      $realpasswd=<$pf>; }
                   2127: 			    chomp($realpasswd);
                   2128: 			    my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
                   2129: 			    if ($howpwd eq 'internal') {
                   2130: 				&Debug("internal auth");
                   2131: 				if (crypt($upass,$contentpwd) eq $contentpwd) {
                   2132: 				    my $salt=time;
                   2133: 				    $salt=substr($salt,6,2);
                   2134: 				    my $ncpass=crypt($npass,$salt);
                   2135: 				    {
                   2136: 					my $pf;
                   2137: 					if ($pf = IO::File->new(">$passfilename")) {
                   2138: 					    print $pf "internal:$ncpass\n";
                   2139: 					    &logthis("Result of password change for $uname: pwchange_success");
                   2140: 					    print $client "ok\n";
                   2141: 					} else {
                   2142: 					    &logthis("Unable to open $uname passwd to change password");
                   2143: 					    print $client "non_authorized\n";
                   2144: 					}
                   2145: 				    }             
                   2146: 				    
                   2147: 				} else {
                   2148: 				    print $client "non_authorized\n";
                   2149: 				}
                   2150: 			    } elsif ($howpwd eq 'unix') {
                   2151: 				# Unix means we have to access /etc/password
                   2152: 				# one way or another.
                   2153: 				# First: Make sure the current password is
                   2154: 				#        correct
                   2155: 				&Debug("auth is unix");
                   2156: 				$contentpwd=(getpwnam($uname))[1];
                   2157: 				my $pwdcorrect = "0";
                   2158: 				my $pwauth_path="/usr/local/sbin/pwauth";
                   2159: 				unless ($contentpwd eq 'x') {
                   2160: 				    $pwdcorrect=
                   2161: 					(crypt($upass,$contentpwd) eq $contentpwd);
                   2162: 				} elsif (-e $pwauth_path) {
                   2163: 				    open PWAUTH, "|$pwauth_path" or
                   2164: 					die "Cannot invoke authentication";
                   2165: 				    print PWAUTH "$uname\n$upass\n";
                   2166: 				    close PWAUTH;
                   2167: 				    &Debug("exited pwauth with $? ($uname,$upass) ");
                   2168: 				    $pwdcorrect=($? == 0);
                   2169: 				}
                   2170: 				if ($pwdcorrect) {
                   2171: 				    my $execdir=$perlvar{'lonDaemons'};
                   2172: 				    &Debug("Opening lcpasswd pipeline");
                   2173: 				    my $pf = IO::File->new("|$execdir/lcpasswd > $perlvar{'lonDaemons'}/logs/lcpasswd.log");
                   2174: 				    print $pf "$uname\n$npass\n$npass\n";
                   2175: 				    close $pf;
                   2176: 				    my $err = $?;
                   2177: 				    my $result = ($err>0 ? 'pwchange_failure' 
                   2178: 						  : 'ok');
                   2179: 				    &logthis("Result of password change for $uname: ".
                   2180: 					     &lcpasswdstrerror($?));
                   2181: 				    print $client "$result\n";
                   2182: 				} else {
                   2183: 				    print $client "non_authorized\n";
                   2184: 				}
                   2185: 			    } else {
                   2186: 				print $client "auth_mode_error\n";
                   2187: 			    }  
                   2188: 			} else {
                   2189: 			    print $client "unknown_user\n";
                   2190: 			}
                   2191: 		    } else {
                   2192: 			Reply($client, "refused\n", $userinput);
                   2193: 		       
                   2194: 		    }
                   2195: # -------------------------------------------------------------------- makeuser
                   2196: 		} elsif ($userinput =~ /^makeuser/) { # encoded and client.
                   2197: 		    &Debug("Make user received");
                   2198: 		    my $oldumask=umask(0077);
                   2199: 		    if (($wasenc==1) && isClient) {
                   2200: 			my 
                   2201: 			    ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
                   2202: 			&Debug("cmd =".$cmd." $udom =".$udom.
                   2203: 			       " uname=".$uname);
                   2204: 			chomp($npass);
                   2205: 			$npass=&unescape($npass);
                   2206: 			my $proname=propath($udom,$uname);
                   2207: 			my $passfilename="$proname/passwd";
                   2208: 			&Debug("Password file created will be:".
                   2209: 			       $passfilename);
                   2210: 			if (-e $passfilename) {
                   2211: 			    print $client "already_exists\n";
                   2212: 			} elsif ($udom ne $currentdomainid) {
                   2213: 			    print $client "not_right_domain\n";
                   2214: 			} else {
                   2215: 			    my @fpparts=split(/\//,$proname);
                   2216: 			    my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];
                   2217: 			    my $fperror='';
                   2218: 			    for (my $i=3;$i<=$#fpparts;$i++) {
                   2219: 				$fpnow.='/'.$fpparts[$i]; 
                   2220: 				unless (-e $fpnow) {
                   2221: 				    unless (mkdir($fpnow,0777)) {
                   2222: 					$fperror="error: ".($!+0)
                   2223: 					    ." mkdir failed while attempting "
                   2224: 					    ."makeuser";
                   2225: 				    }
                   2226: 				}
                   2227: 			    }
                   2228: 			    unless ($fperror) {
                   2229: 				my $result=&make_passwd_file($uname, $umode,$npass,
                   2230: 							     $passfilename);
                   2231: 				print $client $result;
                   2232: 			    } else {
                   2233: 				print $client "$fperror\n";
                   2234: 			    }
                   2235: 			}
                   2236: 		    } else {
                   2237: 			Reply($client, "refused\n", $userinput);
                   2238: 	      
                   2239: 		    }
                   2240: 		    umask($oldumask);
                   2241: # -------------------------------------------------------------- changeuserauth
                   2242: 		} elsif ($userinput =~ /^changeuserauth/) { # encoded & client
                   2243: 		    &Debug("Changing authorization");
                   2244: 		    if (($wasenc==1) && isClient) {
                   2245: 			my 
                   2246: 			    ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
                   2247: 			chomp($npass);
                   2248: 			&Debug("cmd = ".$cmd." domain= ".$udom.
                   2249: 			       "uname =".$uname." umode= ".$umode);
                   2250: 			$npass=&unescape($npass);
                   2251: 			my $proname=&propath($udom,$uname);
                   2252: 			my $passfilename="$proname/passwd";
                   2253: 			if ($udom ne $currentdomainid) {
                   2254: 			    print $client "not_right_domain\n";
                   2255: 			} else {
                   2256: 			    my $result=&make_passwd_file($uname, $umode,$npass,
                   2257: 							 $passfilename);
                   2258: 			    print $client $result;
                   2259: 			}
                   2260: 		    } else {
                   2261: 			Reply($client, "refused\n", $userinput);
                   2262: 		   
                   2263: 		    }
                   2264: # ------------------------------------------------------------------------ home
                   2265: 		} elsif ($userinput =~ /^home/) { # client clear or encoded
                   2266: 		    if(isClient) {
                   2267: 			my ($cmd,$udom,$uname)=split(/:/,$userinput);
                   2268: 			chomp($uname);
                   2269: 			my $proname=propath($udom,$uname);
                   2270: 			if (-e $proname) {
                   2271: 			    print $client "found\n";
                   2272: 			} else {
                   2273: 			    print $client "not_found\n";
                   2274: 			}
                   2275: 		    } else {
                   2276: 			Reply($client, "refused\n", $userinput);
                   2277: 
                   2278: 		    }
                   2279: # ---------------------------------------------------------------------- update
                   2280: 		} elsif ($userinput =~ /^update/) { # client clear or encoded.
                   2281: 		    if(isClient) {
                   2282: 			my ($cmd,$fname)=split(/:/,$userinput);
                   2283: 			my $ownership=ishome($fname);
                   2284: 			if ($ownership eq 'not_owner') {
                   2285: 			    if (-e $fname) {
                   2286: 				my ($dev,$ino,$mode,$nlink,
                   2287: 				    $uid,$gid,$rdev,$size,
                   2288: 				    $atime,$mtime,$ctime,
                   2289: 				    $blksize,$blocks)=stat($fname);
                   2290: 				my $now=time;
                   2291: 				my $since=$now-$atime;
                   2292: 				if ($since>$perlvar{'lonExpire'}) {
                   2293: 				    my $reply=
                   2294: 					&reply("unsub:$fname","$clientname");
                   2295: 				    unlink("$fname");
                   2296: 				} else {
                   2297: 				    my $transname="$fname.in.transfer";
                   2298: 				    my $remoteurl=
                   2299: 					&reply("sub:$fname","$clientname");
                   2300: 				    my $response;
                   2301: 				    {
                   2302: 					my $ua=new LWP::UserAgent;
                   2303: 					my $request=new HTTP::Request('GET',"$remoteurl");
                   2304: 					$response=$ua->request($request,$transname);
                   2305: 				    }
                   2306: 				    if ($response->is_error()) {
                   2307: 					unlink($transname);
                   2308: 					my $message=$response->status_line;
                   2309: 					&logthis(
                   2310: 						 "LWP GET: $message for $fname ($remoteurl)");
                   2311: 				    } else {
                   2312: 					if ($remoteurl!~/\.meta$/) {
                   2313: 					    my $ua=new LWP::UserAgent;
                   2314: 					    my $mrequest=
                   2315: 						new HTTP::Request('GET',$remoteurl.'.meta');
                   2316: 					    my $mresponse=
                   2317: 						$ua->request($mrequest,$fname.'.meta');
                   2318: 					    if ($mresponse->is_error()) {
                   2319: 						unlink($fname.'.meta');
                   2320: 					    }
                   2321: 					}
                   2322: 					rename($transname,$fname);
                   2323: 				    }
                   2324: 				}
                   2325: 				print $client "ok\n";
                   2326: 			    } else {
                   2327: 				print $client "not_found\n";
                   2328: 			    }
                   2329: 			} else {
                   2330: 			    print $client "rejected\n";
                   2331: 			}
                   2332: 		    } else {
                   2333: 			Reply($client, "refused\n", $userinput);
                   2334: 
                   2335: 		    }
                   2336: # -------------------------------------- fetch a user file from a remote server
                   2337: 		} elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc.
                   2338: 		    if(isClient) {
1.184     raeburn  2339: 			my ($cmd,$fname)=split(/:/,$userinput);
1.185     albertel 2340: 			my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|);
1.178     foxr     2341: 			my $udir=propath($udom,$uname).'/userfiles';
                   2342: 			unless (-e $udir) { mkdir($udir,0770); }
                   2343: 			if (-e $udir) {
1.184     raeburn  2344:                             $ufile=~s/^[\.\~]+//;
                   2345:                             my $path = $udir;
1.185     albertel 2346:                             if ($ufile =~m|(.+)/([^/]+)$|) {
                   2347:                                 my @parts=split('/',$1);
1.184     raeburn  2348:                                 foreach my $part (@parts) {
                   2349:                                     $path .= '/'.$part;
                   2350:                                     if ((-e $path)!=1) {
                   2351:                                         mkdir($path,0770);
1.182     raeburn  2352:                                     }
                   2353:                                 }
                   2354:                             }
1.184     raeburn  2355: 			    my $destname=$udir.'/'.$ufile;
                   2356: 			    my $transname=$udir.'/'.$ufile.'.in.transit';
                   2357: 			    my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;
1.178     foxr     2358: 			    my $response;
                   2359: 			    {
                   2360: 				my $ua=new LWP::UserAgent;
                   2361: 				my $request=new HTTP::Request('GET',"$remoteurl");
                   2362: 				$response=$ua->request($request,$transname);
                   2363: 			    }
                   2364: 			    if ($response->is_error()) {
                   2365: 				unlink($transname);
                   2366: 				my $message=$response->status_line;
1.184     raeburn  2367: 				&logthis("LWP GET: $message for $fname ($remoteurl)");
1.178     foxr     2368: 				print $client "failed\n";
                   2369: 			    } else {
                   2370: 				if (!rename($transname,$destname)) {
                   2371: 				    &logthis("Unable to move $transname to $destname");
                   2372: 				    unlink($transname);
                   2373: 				    print $client "failed\n";
                   2374: 				} else {
                   2375: 				    print $client "ok\n";
                   2376: 				}
                   2377: 			    }
                   2378: 			} else {
                   2379: 			    print $client "not_home\n";
1.187     albertel 2380: 			}
                   2381: 		    } else {
                   2382: 			Reply($client, "refused\n", $userinput);
                   2383: 		    }
                   2384: # --------------------------------------------------------- remove a user file 
                   2385: 		} elsif ($userinput =~ /^removeuserfile/) { # Client clear or enc.
                   2386: 		    if(isClient) {
                   2387: 			my ($cmd,$fname)=split(/:/,$userinput);
                   2388: 			my ($udom,$uname,$ufile) = ($fname =~ m|^([^/]+)/([^/]+)/(.+)$|);
                   2389: 			&logthis("$udom - $uname - $ufile");
                   2390: 			if ($ufile =~m|/\.\./|) {
                   2391: 			    # any files paths with /../ in them refuse 
                   2392:                             # to deal with
                   2393: 			    print $client "refused\n";
                   2394: 			} else {
                   2395: 			    my $udir=propath($udom,$uname);
                   2396: 			    if (-e $udir) {
                   2397: 				my $file=$udir.'/userfiles/'.$ufile;
                   2398: 				if (-e $file) {
                   2399: 				    unlink($file);
                   2400: 				    if (-e $file) {
                   2401: 					print $client "failed\n";
                   2402: 				    } else {
                   2403: 					print $client "ok\n";
                   2404: 				    }
                   2405: 				} else {
                   2406: 				    print $client "not_found\n";
                   2407: 				}
                   2408: 			    } else {
                   2409: 				print $client "not_home\n";
                   2410: 			    }
1.178     foxr     2411: 			}
                   2412: 		    } else {
                   2413: 			Reply($client, "refused\n", $userinput);
                   2414: 		    }
                   2415: # ------------------------------------------ authenticate access to a user file
                   2416: 		} elsif ($userinput =~ /^tokenauthuserfile/) { # Client only
                   2417: 		    if(isClient) {
                   2418: 			my ($cmd,$fname,$session)=split(/:/,$userinput);
                   2419: 			chomp($session);
                   2420: 			my $reply='non_auth';
                   2421: 			if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.
                   2422: 				 $session.'.id')) {
                   2423: 			    while (my $line=<ENVIN>) {
1.185     albertel 2424: 				if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; }
1.178     foxr     2425: 			    }
                   2426: 			    close(ENVIN);
                   2427: 			    print $client $reply."\n";
                   2428: 			} else {
                   2429: 			    print $client "invalid_token\n";
                   2430: 			}
                   2431: 		    } else {
                   2432: 			Reply($client, "refused\n", $userinput);
                   2433: 
                   2434: 		    }
                   2435: # ----------------------------------------------------------------- unsubscribe
                   2436: 		} elsif ($userinput =~ /^unsub/) {
                   2437: 		    if(isClient) {
                   2438: 			my ($cmd,$fname)=split(/:/,$userinput);
                   2439: 			if (-e $fname) {
1.188     foxr     2440: 			    print $client &unsub($fname,$clientip);
1.178     foxr     2441: 			} else {
                   2442: 			    print $client "not_found\n";
                   2443: 			}
                   2444: 		    } else {
                   2445: 			Reply($client, "refused\n", $userinput);
                   2446: 
                   2447: 		    }
                   2448: # ------------------------------------------------------------------- subscribe
                   2449: 		} elsif ($userinput =~ /^sub/) {
                   2450: 		    if(isClient) {
                   2451: 			print $client &subscribe($userinput,$clientip);
                   2452: 		    } else {
                   2453: 			Reply($client, "refused\n", $userinput);
                   2454: 
                   2455: 		    }
                   2456: # ------------------------------------------------------------- current version
                   2457: 		} elsif ($userinput =~ /^currentversion/) {
                   2458: 		    if(isClient) {
                   2459: 			my ($cmd,$fname)=split(/:/,$userinput);
                   2460: 			print $client &currentversion($fname)."\n";
                   2461: 		    } else {
                   2462: 			Reply($client, "refused\n", $userinput);
                   2463: 
                   2464: 		    }
                   2465: # ------------------------------------------------------------------------- log
                   2466: 		} elsif ($userinput =~ /^log/) {
                   2467: 		    if(isClient) {
                   2468: 			my ($cmd,$udom,$uname,$what)=split(/:/,$userinput);
                   2469: 			chomp($what);
                   2470: 			my $proname=propath($udom,$uname);
                   2471: 			my $now=time;
                   2472: 			{
                   2473: 			    my $hfh;
                   2474: 			    if ($hfh=IO::File->new(">>$proname/activity.log")) { 
                   2475: 				print $hfh "$now:$clientname:$what\n";
                   2476: 				print $client "ok\n"; 
                   2477: 			    } else {
                   2478: 				print $client "error: ".($!+0)
                   2479: 				    ." IO::File->new Failed "
                   2480: 				    ."while attempting log\n";
                   2481: 			    }
                   2482: 			}
                   2483: 		    } else {
                   2484: 			Reply($client, "refused\n", $userinput);
                   2485: 
                   2486: 		    }
                   2487: # ------------------------------------------------------------------------- put
                   2488: 		} elsif ($userinput =~ /^put/) {
                   2489: 		    if(isClient) {
1.208   ! albertel 2490: 			my ($cmd,$udom,$uname,$namespace,$what)
        !          2491: 			    =split(/:/,$userinput,5);
1.178     foxr     2492: 			$namespace=~s/\//\_/g;
                   2493: 			$namespace=~s/\W//g;
                   2494: 			if ($namespace ne 'roles') {
                   2495: 			    chomp($what);
                   2496: 			    my $proname=propath($udom,$uname);
                   2497: 			    my $now=time;
                   2498: 			    unless ($namespace=~/^nohist\_/) {
                   2499: 				my $hfh;
                   2500: 				if (
                   2501: 				    $hfh=IO::File->new(">>$proname/$namespace.hist")
                   2502: 				    ) { print $hfh "P:$now:$what\n"; }
                   2503: 			    }
                   2504: 			    my @pairs=split(/\&/,$what);
                   2505: 			    my %hash;
                   2506: 			    if (tie(%hash,'GDBM_File',
                   2507: 				    "$proname/$namespace.db",
                   2508: 				    &GDBM_WRCREAT(),0640)) {
                   2509: 				foreach my $pair (@pairs) {
                   2510: 				    my ($key,$value)=split(/=/,$pair);
                   2511: 				    $hash{$key}=$value;
                   2512: 				}
                   2513: 				if (untie(%hash)) {
                   2514: 				    print $client "ok\n";
                   2515: 				} else {
                   2516: 				    print $client "error: ".($!+0)
                   2517: 					." untie(GDBM) failed ".
                   2518: 					"while attempting put\n";
                   2519: 				}
                   2520: 			    } else {
                   2521: 				print $client "error: ".($!)
                   2522: 				    ." tie(GDBM) Failed ".
                   2523: 				    "while attempting put\n";
                   2524: 			    }
                   2525: 			} else {
                   2526: 			    print $client "refused\n";
                   2527: 			}
                   2528: 		    } else {
                   2529: 			Reply($client, "refused\n", $userinput);
                   2530: 
                   2531: 		    }
                   2532: # ------------------------------------------------------------------- inc
                   2533: 		} elsif ($userinput =~ /^inc:/) {
                   2534: 		    if(isClient) {
                   2535: 			my ($cmd,$udom,$uname,$namespace,$what)
                   2536: 			    =split(/:/,$userinput);
                   2537: 			$namespace=~s/\//\_/g;
                   2538: 			$namespace=~s/\W//g;
                   2539: 			if ($namespace ne 'roles') {
                   2540: 			    chomp($what);
                   2541: 			    my $proname=propath($udom,$uname);
                   2542: 			    my $now=time;
                   2543: 			    unless ($namespace=~/^nohist\_/) {
                   2544: 				my $hfh;
                   2545: 				if (
                   2546: 				    $hfh=IO::File->new(">>$proname/$namespace.hist")
                   2547: 				    ) { print $hfh "P:$now:$what\n"; }
                   2548: 			    }
                   2549: 			    my @pairs=split(/\&/,$what);
                   2550: 			    my %hash;
                   2551: 			    if (tie(%hash,'GDBM_File',
                   2552: 				    "$proname/$namespace.db",
                   2553: 				    &GDBM_WRCREAT(),0640)) {
                   2554: 				foreach my $pair (@pairs) {
                   2555: 				    my ($key,$value)=split(/=/,$pair);
                   2556:                                     # We could check that we have a number...
                   2557:                                     if (! defined($value) || $value eq '') {
                   2558:                                         $value = 1;
                   2559:                                     }
                   2560: 				    $hash{$key}+=$value;
                   2561: 				}
                   2562: 				if (untie(%hash)) {
                   2563: 				    print $client "ok\n";
                   2564: 				} else {
                   2565: 				    print $client "error: ".($!+0)
                   2566: 					." untie(GDBM) failed ".
1.181     albertel 2567: 					"while attempting inc\n";
1.178     foxr     2568: 				}
                   2569: 			    } else {
                   2570: 				print $client "error: ".($!)
                   2571: 				    ." tie(GDBM) Failed ".
1.181     albertel 2572: 				    "while attempting inc\n";
1.178     foxr     2573: 			    }
                   2574: 			} else {
                   2575: 			    print $client "refused\n";
                   2576: 			}
                   2577: 		    } else {
                   2578: 			Reply($client, "refused\n", $userinput);
                   2579: 
                   2580: 		    }
                   2581: # -------------------------------------------------------------------- rolesput
                   2582: 		} elsif ($userinput =~ /^rolesput/) {
                   2583: 		    if(isClient) {
                   2584: 			&Debug("rolesput");
                   2585: 			if ($wasenc==1) {
                   2586: 			    my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
                   2587: 				=split(/:/,$userinput);
                   2588: 			    &Debug("cmd = ".$cmd." exedom= ".$exedom.
                   2589: 				   "user = ".$exeuser." udom=".$udom.
                   2590: 				   "what = ".$what);
                   2591: 			    my $namespace='roles';
                   2592: 			    chomp($what);
                   2593: 			    my $proname=propath($udom,$uname);
                   2594: 			    my $now=time;
                   2595: 			    {
                   2596: 				my $hfh;
                   2597: 				if (
                   2598: 				    $hfh=IO::File->new(">>$proname/$namespace.hist")
                   2599: 				    ) { 
                   2600: 				    print $hfh "P:$now:$exedom:$exeuser:$what\n";
                   2601: 				}
                   2602: 			    }
                   2603: 			    my @pairs=split(/\&/,$what);
                   2604: 			    my %hash;
                   2605: 			    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
                   2606: 				foreach my $pair (@pairs) {
                   2607: 				    my ($key,$value)=split(/=/,$pair);
                   2608: 				    &ManagePermissions($key, $udom, $uname,
                   2609: 						       &GetAuthType( $udom, 
                   2610: 								     $uname));
                   2611: 				    $hash{$key}=$value;
                   2612: 				}
                   2613: 				if (untie(%hash)) {
                   2614: 				    print $client "ok\n";
                   2615: 				} else {
                   2616: 				    print $client "error: ".($!+0)
                   2617: 					." untie(GDBM) Failed ".
                   2618: 					"while attempting rolesput\n";
                   2619: 				}
                   2620: 			    } else {
                   2621: 				print $client "error: ".($!+0)
                   2622: 				    ." tie(GDBM) Failed ".
                   2623: 				    "while attempting rolesput\n";
                   2624: 			    }
                   2625: 			} else {
                   2626: 			    print $client "refused\n";
                   2627: 			}
                   2628: 		    } else {
                   2629: 			Reply($client, "refused\n", $userinput);
                   2630: 		  
                   2631: 		    }
                   2632: # -------------------------------------------------------------------- rolesdel
                   2633: 		} elsif ($userinput =~ /^rolesdel/) {
                   2634: 		    if(isClient) {
                   2635: 			&Debug("rolesdel");
                   2636: 			if ($wasenc==1) {
                   2637: 			    my ($cmd,$exedom,$exeuser,$udom,$uname,$what)
                   2638: 				=split(/:/,$userinput);
                   2639: 			    &Debug("cmd = ".$cmd." exedom= ".$exedom.
                   2640: 				   "user = ".$exeuser." udom=".$udom.
                   2641: 				   "what = ".$what);
                   2642: 			    my $namespace='roles';
                   2643: 			    chomp($what);
                   2644: 			    my $proname=propath($udom,$uname);
                   2645: 			    my $now=time;
                   2646: 			    {
                   2647: 				my $hfh;
                   2648: 				if (
                   2649: 				    $hfh=IO::File->new(">>$proname/$namespace.hist")
                   2650: 				    ) { 
                   2651: 				    print $hfh "D:$now:$exedom:$exeuser:$what\n";
                   2652: 				}
                   2653: 			    }
                   2654: 			    my @rolekeys=split(/\&/,$what);
                   2655: 			    my %hash;
                   2656: 			    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
                   2657: 				foreach my $key (@rolekeys) {
                   2658: 				    delete $hash{$key};
                   2659: 				}
                   2660: 				if (untie(%hash)) {
                   2661: 				    print $client "ok\n";
                   2662: 				} else {
                   2663: 				    print $client "error: ".($!+0)
                   2664: 					." untie(GDBM) Failed ".
                   2665: 					"while attempting rolesdel\n";
                   2666: 				}
                   2667: 			    } else {
                   2668: 				print $client "error: ".($!+0)
                   2669: 				    ." tie(GDBM) Failed ".
                   2670: 				    "while attempting rolesdel\n";
                   2671: 			    }
                   2672: 			} else {
                   2673: 			    print $client "refused\n";
                   2674: 			}
                   2675: 		    } else {
                   2676: 			Reply($client, "refused\n", $userinput);
                   2677: 		      
                   2678: 		    }
                   2679: # ------------------------------------------------------------------------- get
                   2680: 		} elsif ($userinput =~ /^get/) {
                   2681: 		    if(isClient) {
                   2682: 			my ($cmd,$udom,$uname,$namespace,$what)
                   2683: 			    =split(/:/,$userinput);
                   2684: 			$namespace=~s/\//\_/g;
                   2685: 			$namespace=~s/\W//g;
                   2686: 			chomp($what);
                   2687: 			my @queries=split(/\&/,$what);
                   2688: 			my $proname=propath($udom,$uname);
                   2689: 			my $qresult='';
                   2690: 			my %hash;
                   2691: 			if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
                   2692: 			    for (my $i=0;$i<=$#queries;$i++) {
                   2693: 				$qresult.="$hash{$queries[$i]}&";
                   2694: 			    }
                   2695: 			    if (untie(%hash)) {
                   2696: 				$qresult=~s/\&$//;
                   2697: 				print $client "$qresult\n";
                   2698: 			    } else {
                   2699: 				print $client "error: ".($!+0)
                   2700: 				    ." untie(GDBM) Failed ".
                   2701: 				    "while attempting get\n";
                   2702: 			    }
                   2703: 			} else {
                   2704: 			    if ($!+0 == 2) {
                   2705: 				print $client "error:No such file or ".
                   2706: 				    "GDBM reported bad block error\n";
                   2707: 			    } else {
                   2708: 				print $client "error: ".($!+0)
                   2709: 				    ." tie(GDBM) Failed ".
                   2710: 				    "while attempting get\n";
                   2711: 			    }
                   2712: 			}
                   2713: 		    } else {
                   2714: 			Reply($client, "refused\n", $userinput);
                   2715: 		       
                   2716: 		    }
                   2717: # ------------------------------------------------------------------------ eget
                   2718: 		} elsif ($userinput =~ /^eget/) {
                   2719: 		    if (isClient) {
                   2720: 			my ($cmd,$udom,$uname,$namespace,$what)
                   2721: 			    =split(/:/,$userinput);
                   2722: 			$namespace=~s/\//\_/g;
                   2723: 			$namespace=~s/\W//g;
                   2724: 			chomp($what);
                   2725: 			my @queries=split(/\&/,$what);
                   2726: 			my $proname=propath($udom,$uname);
                   2727: 			my $qresult='';
                   2728: 			my %hash;
                   2729: 			if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
                   2730: 			    for (my $i=0;$i<=$#queries;$i++) {
                   2731: 				$qresult.="$hash{$queries[$i]}&";
                   2732: 			    }
                   2733: 			    if (untie(%hash)) {
                   2734: 				$qresult=~s/\&$//;
                   2735: 				if ($cipher) {
                   2736: 				    my $cmdlength=length($qresult);
                   2737: 				    $qresult.="         ";
                   2738: 				    my $encqresult='';
                   2739: 				    for 
                   2740: 					(my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
                   2741: 					    $encqresult.=
                   2742: 						unpack("H16",
                   2743: 						       $cipher->encrypt(substr($qresult,$encidx,8)));
                   2744: 					}
                   2745: 				    print $client "enc:$cmdlength:$encqresult\n";
                   2746: 				} else {
                   2747: 				    print $client "error:no_key\n";
                   2748: 				}
                   2749: 			    } else {
                   2750: 				print $client "error: ".($!+0)
                   2751: 				    ." untie(GDBM) Failed ".
                   2752: 				    "while attempting eget\n";
                   2753: 			    }
                   2754: 			} else {
                   2755: 			    print $client "error: ".($!+0)
                   2756: 				." tie(GDBM) Failed ".
                   2757: 				"while attempting eget\n";
                   2758: 			}
                   2759: 		    } else {
                   2760: 			Reply($client, "refused\n", $userinput);
                   2761: 		    
                   2762: 		    }
                   2763: # ------------------------------------------------------------------------- del
                   2764: 		} elsif ($userinput =~ /^del/) {
                   2765: 		    if(isClient) {
                   2766: 			my ($cmd,$udom,$uname,$namespace,$what)
                   2767: 			    =split(/:/,$userinput);
                   2768: 			$namespace=~s/\//\_/g;
                   2769: 			$namespace=~s/\W//g;
                   2770: 			chomp($what);
                   2771: 			my $proname=propath($udom,$uname);
                   2772: 			my $now=time;
                   2773: 			unless ($namespace=~/^nohist\_/) {
                   2774: 			    my $hfh;
                   2775: 			    if (
                   2776: 				$hfh=IO::File->new(">>$proname/$namespace.hist")
                   2777: 				) { print $hfh "D:$now:$what\n"; }
                   2778: 			}
                   2779: 			my @keys=split(/\&/,$what);
                   2780: 			my %hash;
                   2781: 			if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
                   2782: 			    foreach my $key (@keys) {
                   2783: 				delete($hash{$key});
                   2784: 			    }
                   2785: 			    if (untie(%hash)) {
                   2786: 				print $client "ok\n";
                   2787: 			    } else {
                   2788: 				print $client "error: ".($!+0)
                   2789: 				    ." untie(GDBM) Failed ".
                   2790: 				    "while attempting del\n";
                   2791: 			    }
                   2792: 			} else {
                   2793: 			    print $client "error: ".($!+0)
                   2794: 				." tie(GDBM) Failed ".
                   2795: 				"while attempting del\n";
                   2796: 			}
                   2797: 		    } else {
                   2798: 			Reply($client, "refused\n", $userinput);
                   2799: 			
                   2800: 		    }
                   2801: # ------------------------------------------------------------------------ keys
                   2802: 		} elsif ($userinput =~ /^keys/) {
                   2803: 		    if(isClient) {
                   2804: 			my ($cmd,$udom,$uname,$namespace)
                   2805: 			    =split(/:/,$userinput);
                   2806: 			$namespace=~s/\//\_/g;
                   2807: 			$namespace=~s/\W//g;
                   2808: 			my $proname=propath($udom,$uname);
                   2809: 			my $qresult='';
                   2810: 			my %hash;
                   2811: 			if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
                   2812: 			    foreach my $key (keys %hash) {
                   2813: 				$qresult.="$key&";
                   2814: 			    }
                   2815: 			    if (untie(%hash)) {
                   2816: 				$qresult=~s/\&$//;
                   2817: 				print $client "$qresult\n";
                   2818: 			    } else {
                   2819: 				print $client "error: ".($!+0)
                   2820: 				    ." untie(GDBM) Failed ".
                   2821: 				    "while attempting keys\n";
                   2822: 			    }
                   2823: 			} else {
                   2824: 			    print $client "error: ".($!+0)
                   2825: 				." tie(GDBM) Failed ".
                   2826: 				"while attempting keys\n";
                   2827: 			}
                   2828: 		    } else {
                   2829: 			Reply($client, "refused\n", $userinput);
                   2830: 		   
                   2831: 		    }
                   2832: # ----------------------------------------------------------------- dumpcurrent
                   2833: 		} elsif ($userinput =~ /^currentdump/) {
                   2834: 		    if (isClient) {
                   2835: 			my ($cmd,$udom,$uname,$namespace)
                   2836: 			    =split(/:/,$userinput);
                   2837: 			$namespace=~s/\//\_/g;
                   2838: 			$namespace=~s/\W//g;
                   2839: 			my $qresult='';
                   2840: 			my $proname=propath($udom,$uname);
                   2841: 			my %hash;
                   2842: 			if (tie(%hash,'GDBM_File',
                   2843: 				"$proname/$namespace.db",
                   2844: 				&GDBM_READER(),0640)) {
                   2845: 			    # Structure of %data:
                   2846: 			    # $data{$symb}->{$parameter}=$value;
                   2847: 			    # $data{$symb}->{'v.'.$parameter}=$version;
                   2848: 			    # since $parameter will be unescaped, we do not
                   2849: 			    # have to worry about silly parameter names...
                   2850: 			    my %data = ();
                   2851: 			    while (my ($key,$value) = each(%hash)) {
                   2852: 				my ($v,$symb,$param) = split(/:/,$key);
                   2853: 				next if ($v eq 'version' || $symb eq 'keys');
                   2854: 				next if (exists($data{$symb}) && 
                   2855: 					 exists($data{$symb}->{$param}) &&
                   2856: 					 $data{$symb}->{'v.'.$param} > $v);
                   2857: 				$data{$symb}->{$param}=$value;
                   2858: 				$data{$symb}->{'v.'.$param}=$v;
                   2859: 			    }
                   2860: 			    if (untie(%hash)) {
                   2861: 				while (my ($symb,$param_hash) = each(%data)) {
                   2862: 				    while(my ($param,$value) = each (%$param_hash)){
                   2863: 					next if ($param =~ /^v\./);
                   2864: 					$qresult.=$symb.':'.$param.'='.$value.'&';
                   2865: 				    }
                   2866: 				}
                   2867: 				chop($qresult);
                   2868: 				print $client "$qresult\n";
                   2869: 			    } else {
                   2870: 				print $client "error: ".($!+0)
                   2871: 				    ." untie(GDBM) Failed ".
                   2872: 				    "while attempting currentdump\n";
                   2873: 			    }
                   2874: 			} else {
                   2875: 			    print $client "error: ".($!+0)
                   2876: 				." tie(GDBM) Failed ".
                   2877: 				"while attempting currentdump\n";
                   2878: 			}
                   2879: 		    } else {
                   2880: 			Reply($client, "refused\n", $userinput);
                   2881: 		    }
                   2882: # ------------------------------------------------------------------------ dump
                   2883: 		} elsif ($userinput =~ /^dump/) {
                   2884: 		    if(isClient) {
                   2885: 			my ($cmd,$udom,$uname,$namespace,$regexp)
                   2886: 			    =split(/:/,$userinput);
                   2887: 			$namespace=~s/\//\_/g;
                   2888: 			$namespace=~s/\W//g;
                   2889: 			if (defined($regexp)) {
                   2890: 			    $regexp=&unescape($regexp);
                   2891: 			} else {
                   2892: 			    $regexp='.';
                   2893: 			}
                   2894: 			my $qresult='';
                   2895: 			my $proname=propath($udom,$uname);
                   2896: 			my %hash;
                   2897: 			if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
                   2898: 			       while (my ($key,$value) = each(%hash)) {
                   2899: 				   if ($regexp eq '.') {
                   2900: 				       $qresult.=$key.'='.$value.'&';
                   2901: 				   } else {
                   2902: 				       my $unescapeKey = &unescape($key);
                   2903: 				       if (eval('$unescapeKey=~/$regexp/')) {
                   2904: 					   $qresult.="$key=$value&";
                   2905: 				       }
                   2906: 				   }
                   2907: 			       }
                   2908: 			       if (untie(%hash)) {
                   2909: 				   chop($qresult);
                   2910: 				   print $client "$qresult\n";
                   2911: 			       } else {
                   2912: 				   print $client "error: ".($!+0)
                   2913: 				       ." untie(GDBM) Failed ".
                   2914:                                        "while attempting dump\n";
                   2915: 			       }
                   2916: 			   } else {
                   2917: 			       print $client "error: ".($!+0)
                   2918: 				   ." tie(GDBM) Failed ".
                   2919: 				   "while attempting dump\n";
                   2920: 			   }
                   2921: 		    } else {
                   2922: 			Reply($client, "refused\n", $userinput);
                   2923: 		 
                   2924: 		    }
                   2925: # ----------------------------------------------------------------------- store
                   2926: 		} elsif ($userinput =~ /^store/) {
                   2927: 		    if(isClient) {
                   2928: 			my ($cmd,$udom,$uname,$namespace,$rid,$what)
                   2929: 			    =split(/:/,$userinput);
                   2930: 			$namespace=~s/\//\_/g;
                   2931: 			$namespace=~s/\W//g;
                   2932: 			if ($namespace ne 'roles') {
                   2933: 			    chomp($what);
                   2934: 			    my $proname=propath($udom,$uname);
                   2935: 			    my $now=time;
                   2936: 			    unless ($namespace=~/^nohist\_/) {
                   2937: 				my $hfh;
                   2938: 				if (
                   2939: 				    $hfh=IO::File->new(">>$proname/$namespace.hist")
                   2940: 				    ) { print $hfh "P:$now:$rid:$what\n"; }
                   2941: 			    }
                   2942: 			    my @pairs=split(/\&/,$what);
                   2943: 			    my %hash;
                   2944: 			    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) {
                   2945: 				my @previouskeys=split(/&/,$hash{"keys:$rid"});
                   2946: 				my $key;
                   2947: 				$hash{"version:$rid"}++;
                   2948: 				my $version=$hash{"version:$rid"};
                   2949: 				my $allkeys=''; 
                   2950: 				foreach my $pair (@pairs) {
                   2951: 				    my ($key,$value)=split(/=/,$pair);
                   2952: 				    $allkeys.=$key.':';
                   2953: 				    $hash{"$version:$rid:$key"}=$value;
                   2954: 				}
                   2955: 				$hash{"$version:$rid:timestamp"}=$now;
                   2956: 				$allkeys.='timestamp';
                   2957: 				$hash{"$version:keys:$rid"}=$allkeys;
                   2958: 				if (untie(%hash)) {
                   2959: 				    print $client "ok\n";
                   2960: 				} else {
                   2961: 				    print $client "error: ".($!+0)
                   2962: 					." untie(GDBM) Failed ".
                   2963: 					"while attempting store\n";
                   2964: 				}
                   2965: 			    } else {
                   2966: 				print $client "error: ".($!+0)
                   2967: 				    ." tie(GDBM) Failed ".
                   2968: 				    "while attempting store\n";
                   2969: 			    }
                   2970: 			} else {
                   2971: 			    print $client "refused\n";
                   2972: 			}
                   2973: 		    } else {
                   2974: 			Reply($client, "refused\n", $userinput);
                   2975: 		     
                   2976: 		    }
                   2977: # --------------------------------------------------------------------- restore
                   2978: 		} elsif ($userinput =~ /^restore/) {
                   2979: 		    if(isClient) {
                   2980: 			my ($cmd,$udom,$uname,$namespace,$rid)
                   2981: 			    =split(/:/,$userinput);
                   2982: 			$namespace=~s/\//\_/g;
                   2983: 			$namespace=~s/\W//g;
                   2984: 			chomp($rid);
                   2985: 			my $proname=propath($udom,$uname);
                   2986: 			my $qresult='';
                   2987: 			my %hash;
                   2988: 			if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) {
                   2989: 			    my $version=$hash{"version:$rid"};
                   2990: 			    $qresult.="version=$version&";
                   2991: 			    my $scope;
                   2992: 			    for ($scope=1;$scope<=$version;$scope++) {
                   2993: 				my $vkeys=$hash{"$scope:keys:$rid"};
                   2994: 				my @keys=split(/:/,$vkeys);
                   2995: 				my $key;
                   2996: 				$qresult.="$scope:keys=$vkeys&";
                   2997: 				foreach $key (@keys) {
                   2998: 				    $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
                   2999: 				}                                  
                   3000: 			    }
                   3001: 			    if (untie(%hash)) {
                   3002: 				$qresult=~s/\&$//;
                   3003: 				print $client "$qresult\n";
                   3004: 			    } else {
                   3005: 				print $client "error: ".($!+0)
                   3006: 				    ." untie(GDBM) Failed ".
                   3007: 				    "while attempting restore\n";
                   3008: 			    }
                   3009: 			} else {
                   3010: 			    print $client "error: ".($!+0)
                   3011: 				." tie(GDBM) Failed ".
                   3012: 				"while attempting restore\n";
                   3013: 			}
                   3014: 		    } else  {
                   3015: 			Reply($client, "refused\n", $userinput);
                   3016: 		       
                   3017: 		    }
                   3018: # -------------------------------------------------------------------- chatsend
                   3019: 		} elsif ($userinput =~ /^chatsend/) {
                   3020: 		    if(isClient) {
                   3021: 			my ($cmd,$cdom,$cnum,$newpost)=split(/\:/,$userinput);
                   3022: 			&chatadd($cdom,$cnum,$newpost);
                   3023: 			print $client "ok\n";
                   3024: 		    } else {
                   3025: 			Reply($client, "refused\n", $userinput);
                   3026: 		      
                   3027: 		    }
                   3028: # -------------------------------------------------------------------- chatretr
                   3029: 		} elsif ($userinput =~ /^chatretr/) {
                   3030: 		    if(isClient) {
                   3031: 			my 
                   3032: 			    ($cmd,$cdom,$cnum,$udom,$uname)=split(/\:/,$userinput);
                   3033: 			my $reply='';
                   3034: 			foreach (&getchat($cdom,$cnum,$udom,$uname)) {
                   3035: 			    $reply.=&escape($_).':';
                   3036: 			}
                   3037: 			$reply=~s/\:$//;
                   3038: 			print $client $reply."\n";
                   3039: 		    } else {
                   3040: 			Reply($client, "refused\n", $userinput);
                   3041: 		       
                   3042: 		    }
                   3043: # ------------------------------------------------------------------- querysend
                   3044: 		} elsif ($userinput =~ /^querysend/) {
1.193     raeburn  3045: 		    if (isClient) {
1.178     foxr     3046: 			my ($cmd,$query,
                   3047: 			    $arg1,$arg2,$arg3)=split(/\:/,$userinput);
                   3048: 			$query=~s/\n*$//g;
                   3049: 			print $client "".
                   3050: 			    sqlreply("$clientname\&$query".
                   3051: 				     "\&$arg1"."\&$arg2"."\&$arg3")."\n";
                   3052: 		    } else {
                   3053: 			Reply($client, "refused\n", $userinput);
                   3054: 		      
                   3055: 		    }
                   3056: # ------------------------------------------------------------------ queryreply
                   3057: 		} elsif ($userinput =~ /^queryreply/) {
                   3058: 		    if(isClient) {
                   3059: 			my ($cmd,$id,$reply)=split(/:/,$userinput); 
                   3060: 			my $store;
                   3061: 			my $execdir=$perlvar{'lonDaemons'};
                   3062: 			if ($store=IO::File->new(">$execdir/tmp/$id")) {
                   3063: 			    $reply=~s/\&/\n/g;
                   3064: 			    print $store $reply;
                   3065: 			    close $store;
                   3066: 			    my $store2=IO::File->new(">$execdir/tmp/$id.end");
                   3067: 			    print $store2 "done\n";
                   3068: 			    close $store2;
                   3069: 			    print $client "ok\n";
                   3070: 			}
                   3071: 			else {
                   3072: 			    print $client "error: ".($!+0)
                   3073: 				." IO::File->new Failed ".
                   3074: 				"while attempting queryreply\n";
                   3075: 			}
                   3076: 		    } else {
                   3077: 			Reply($client, "refused\n", $userinput);
                   3078: 		     
                   3079: 		    }
                   3080: # ----------------------------------------------------------------- courseidput
                   3081: 		} elsif ($userinput =~ /^courseidput/) {
                   3082: 		    if(isClient) {
                   3083: 			my ($cmd,$udom,$what)=split(/:/,$userinput);
                   3084: 			chomp($what);
                   3085: 			$udom=~s/\W//g;
                   3086: 			my $proname=
                   3087: 			    "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
                   3088: 			my $now=time;
                   3089: 			my @pairs=split(/\&/,$what);
                   3090: 			my %hash;
                   3091: 			if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
                   3092: 			    foreach my $pair (@pairs) {
1.202     raeburn  3093: 				my ($key,$descr,$inst_code)=split(/=/,$pair);
                   3094: 				$hash{$key}=$descr.':'.$inst_code.':'.$now;
1.178     foxr     3095: 			    }
                   3096: 			    if (untie(%hash)) {
                   3097: 				print $client "ok\n";
                   3098: 			    } else {
                   3099: 				print $client "error: ".($!+0)
                   3100: 				    ." untie(GDBM) Failed ".
                   3101: 				    "while attempting courseidput\n";
                   3102: 			    }
                   3103: 			} else {
                   3104: 			    print $client "error: ".($!+0)
                   3105: 				." tie(GDBM) Failed ".
                   3106: 				"while attempting courseidput\n";
                   3107: 			}
                   3108: 		    } else {
                   3109: 			Reply($client, "refused\n", $userinput);
                   3110: 		       
                   3111: 		    }
                   3112: # ---------------------------------------------------------------- courseiddump
                   3113: 		} elsif ($userinput =~ /^courseiddump/) {
                   3114: 		    if(isClient) {
                   3115: 			my ($cmd,$udom,$since,$description)
                   3116: 			    =split(/:/,$userinput);
                   3117: 			if (defined($description)) {
                   3118: 			    $description=&unescape($description);
                   3119: 			} else {
                   3120: 			    $description='.';
                   3121: 			}
                   3122: 			unless (defined($since)) { $since=0; }
                   3123: 			my $qresult='';
                   3124: 			my $proname=
                   3125: 			    "$perlvar{'lonUsersDir'}/$udom/nohist_courseids";
                   3126: 			my %hash;
                   3127: 			if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
                   3128: 			    while (my ($key,$value) = each(%hash)) {
1.202     raeburn  3129:                                 my ($descr,$lasttime,$inst_code);
                   3130:                                 if ($value =~ m/^([^\:]*):([^\:]*):(\d+)$/) {
                   3131: 				    ($descr,$inst_code,$lasttime)=($1,$2,$3);
                   3132:                                 } else {
                   3133:                                     ($descr,$lasttime) = split(/\:/,$value);
                   3134:                                 }
1.178     foxr     3135: 				if ($lasttime<$since) { next; }
                   3136: 				if ($description eq '.') {
1.202     raeburn  3137: 				    $qresult.=$key.'='.$descr.':'.$inst_code.'&';
1.178     foxr     3138: 				} else {
                   3139: 				    my $unescapeVal = &unescape($descr);
1.189     www      3140: 				    if (eval('$unescapeVal=~/\Q$description\E/i')) {
1.202     raeburn  3141: 					$qresult.=$key.'='.$descr.':'.$inst_code.'&';
1.178     foxr     3142: 				    }
                   3143: 				}
                   3144: 			    }
                   3145: 			    if (untie(%hash)) {
                   3146: 				chop($qresult);
                   3147: 				print $client "$qresult\n";
                   3148: 			    } else {
                   3149: 				print $client "error: ".($!+0)
                   3150: 				    ." untie(GDBM) Failed ".
                   3151: 				    "while attempting courseiddump\n";
                   3152: 			    }
                   3153: 			} else {
                   3154: 			    print $client "error: ".($!+0)
                   3155: 				." tie(GDBM) Failed ".
                   3156: 				"while attempting courseiddump\n";
                   3157: 			}
                   3158: 		    } else {
                   3159: 			Reply($client, "refused\n", $userinput);
                   3160: 		       
                   3161: 		    }
                   3162: # ----------------------------------------------------------------------- idput
                   3163: 		} elsif ($userinput =~ /^idput/) {
                   3164: 		    if(isClient) {
                   3165: 			my ($cmd,$udom,$what)=split(/:/,$userinput);
                   3166: 			chomp($what);
                   3167: 			$udom=~s/\W//g;
                   3168: 			my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
                   3169: 			my $now=time;
                   3170: 			{
                   3171: 			    my $hfh;
                   3172: 			    if (
                   3173: 				$hfh=IO::File->new(">>$proname.hist")
                   3174: 				) { print $hfh "P:$now:$what\n"; }
                   3175: 			}
                   3176: 			my @pairs=split(/\&/,$what);
                   3177: 			my %hash;
                   3178: 			if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) {
                   3179: 			    foreach my $pair (@pairs) {
                   3180: 				my ($key,$value)=split(/=/,$pair);
                   3181: 				$hash{$key}=$value;
                   3182: 			    }
                   3183: 			    if (untie(%hash)) {
                   3184: 				print $client "ok\n";
                   3185: 			    } else {
                   3186: 				print $client "error: ".($!+0)
                   3187: 				    ." untie(GDBM) Failed ".
                   3188: 				    "while attempting idput\n";
                   3189: 			    }
                   3190: 			} else {
                   3191: 			    print $client "error: ".($!+0)
                   3192: 				." tie(GDBM) Failed ".
                   3193: 				"while attempting idput\n";
                   3194: 			}
                   3195: 		    } else {
                   3196: 			Reply($client, "refused\n", $userinput);
                   3197: 		       
                   3198: 		    }
                   3199: # ----------------------------------------------------------------------- idget
                   3200: 		} elsif ($userinput =~ /^idget/) {
                   3201: 		    if(isClient) {
                   3202: 			my ($cmd,$udom,$what)=split(/:/,$userinput);
                   3203: 			chomp($what);
                   3204: 			$udom=~s/\W//g;
                   3205: 			my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
                   3206: 			my @queries=split(/\&/,$what);
                   3207: 			my $qresult='';
                   3208: 			my %hash;
                   3209: 			if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) {
                   3210: 			    for (my $i=0;$i<=$#queries;$i++) {
                   3211: 				$qresult.="$hash{$queries[$i]}&";
                   3212: 			    }
                   3213: 			    if (untie(%hash)) {
                   3214: 				$qresult=~s/\&$//;
                   3215: 				print $client "$qresult\n";
                   3216: 			    } else {
                   3217: 				print $client "error: ".($!+0)
                   3218: 				    ." untie(GDBM) Failed ".
                   3219: 				    "while attempting idget\n";
                   3220: 			    }
                   3221: 			} else {
                   3222: 			    print $client "error: ".($!+0)
                   3223: 				." tie(GDBM) Failed ".
                   3224: 				"while attempting idget\n";
                   3225: 			}
                   3226: 		    } else {
                   3227: 			Reply($client, "refused\n", $userinput);
                   3228: 		       
                   3229: 		    }
                   3230: # ---------------------------------------------------------------------- tmpput
                   3231: 		} elsif ($userinput =~ /^tmpput/) {
                   3232: 		    if(isClient) {
                   3233: 			my ($cmd,$what)=split(/:/,$userinput);
                   3234: 			my $store;
                   3235: 			$tmpsnum++;
                   3236: 			my $id=$$.'_'.$clientip.'_'.$tmpsnum;
                   3237: 			$id=~s/\W/\_/g;
                   3238: 			$what=~s/\n//g;
                   3239: 			my $execdir=$perlvar{'lonDaemons'};
                   3240: 			if ($store=IO::File->new(">$execdir/tmp/$id.tmp")) {
                   3241: 			    print $store $what;
                   3242: 			    close $store;
                   3243: 			    print $client "$id\n";
                   3244: 			}
                   3245: 			else {
                   3246: 			    print $client "error: ".($!+0)
                   3247: 				."IO::File->new Failed ".
                   3248: 				"while attempting tmpput\n";
                   3249: 			}
                   3250: 		    } else {
                   3251: 			Reply($client, "refused\n", $userinput);
                   3252: 		    
                   3253: 		    }
                   3254: 		    
                   3255: # ---------------------------------------------------------------------- tmpget
                   3256: 		} elsif ($userinput =~ /^tmpget/) {
                   3257: 		    if(isClient) {
                   3258: 			my ($cmd,$id)=split(/:/,$userinput);
                   3259: 			chomp($id);
                   3260: 			$id=~s/\W/\_/g;
                   3261: 			my $store;
                   3262: 			my $execdir=$perlvar{'lonDaemons'};
                   3263: 			if ($store=IO::File->new("$execdir/tmp/$id.tmp")) {
                   3264: 			    my $reply=<$store>;
                   3265: 			    print $client "$reply\n";
                   3266: 			    close $store;
                   3267: 			}
                   3268: 			else {
                   3269: 			    print $client "error: ".($!+0)
                   3270: 				."IO::File->new Failed ".
                   3271: 				"while attempting tmpget\n";
                   3272: 			}
                   3273: 		    } else {
                   3274: 			Reply($client, "refused\n", $userinput);
                   3275: 		      
                   3276: 		    }
                   3277: # ---------------------------------------------------------------------- tmpdel
                   3278: 		} elsif ($userinput =~ /^tmpdel/) {
                   3279: 		    if(isClient) {
                   3280: 			my ($cmd,$id)=split(/:/,$userinput);
                   3281: 			chomp($id);
                   3282: 			$id=~s/\W/\_/g;
                   3283: 			my $execdir=$perlvar{'lonDaemons'};
                   3284: 			if (unlink("$execdir/tmp/$id.tmp")) {
                   3285: 			    print $client "ok\n";
                   3286: 			} else {
                   3287: 			    print $client "error: ".($!+0)
                   3288: 				."Unlink tmp Failed ".
                   3289: 				"while attempting tmpdel\n";
                   3290: 			}
                   3291: 		    } else {
                   3292: 			Reply($client, "refused\n", $userinput);
                   3293: 		     
                   3294: 		    }
1.201     matthew  3295: # ----------------------------------------- portfolio directory list (portls)
                   3296:                 } elsif ($userinput =~ /^portls/) {
                   3297:                     if(isClient) {
                   3298:                         my ($cmd,$uname,$udom)=split(/:/,$userinput);
                   3299:                         my $udir=propath($udom,$uname).'/userfiles/portfolio';
                   3300:                         my $dirLine='';
                   3301:                         my $dirContents='';
                   3302:                         if (opendir(LSDIR,$udir.'/')){
                   3303:                             while ($dirLine = readdir(LSDIR)){
                   3304:                                 $dirContents = $dirContents.$dirLine.'<br />';
                   3305:                             }
                   3306:                         } else {
                   3307:                             $dirContents = "No directory found\n";
                   3308:                         }
                   3309:                         print $client $dirContents."\n";
                   3310:                     } else {
                   3311:                         Reply($client, "refused\n", $userinput);
                   3312:                     }
1.178     foxr     3313: # -------------------------------------------------------------------------- ls
                   3314: 		} elsif ($userinput =~ /^ls/) {
                   3315: 		    if(isClient) {
                   3316: 			my $obs;
                   3317: 			my $rights;
                   3318: 			my ($cmd,$ulsdir)=split(/:/,$userinput);
                   3319: 			my $ulsout='';
                   3320: 			my $ulsfn;
                   3321: 			if (-e $ulsdir) {
                   3322: 			    if(-d $ulsdir) {
                   3323: 				if (opendir(LSDIR,$ulsdir)) {
                   3324: 				    while ($ulsfn=readdir(LSDIR)) {
                   3325: 					undef $obs, $rights; 
                   3326: 					my @ulsstats=stat($ulsdir.'/'.$ulsfn);
                   3327: 					#We do some obsolete checking here
                   3328: 					if(-e $ulsdir.'/'.$ulsfn.".meta") { 
                   3329: 					    open(FILE, $ulsdir.'/'.$ulsfn.".meta");
                   3330: 					    my @obsolete=<FILE>;
                   3331: 					    foreach my $obsolete (@obsolete) {
                   3332: 					        if($obsolete =~ m|(<obsolete>)(on)|) { $obs = 1; } 
                   3333: 						if($obsolete =~ m|(<copyright>)(default)|) { $rights = 1; }
                   3334: 					    }
                   3335: 					}
                   3336: 					$ulsout.=$ulsfn.'&'.join('&',@ulsstats);
                   3337: 					if($obs eq '1') { $ulsout.="&1"; }
                   3338: 					else { $ulsout.="&0"; }
                   3339: 					if($rights eq '1') { $ulsout.="&1:"; }
                   3340: 					else { $ulsout.="&0:"; }
                   3341: 				    }
                   3342: 				    closedir(LSDIR);
                   3343: 				}
                   3344: 			    } else {
                   3345: 				my @ulsstats=stat($ulsdir);
                   3346: 				$ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
                   3347: 			    }
                   3348: 			} else {
                   3349: 			    $ulsout='no_such_dir';
                   3350: 			}
                   3351: 			if ($ulsout eq '') { $ulsout='empty'; }
                   3352: 			print $client "$ulsout\n";
                   3353: 		    } else {
                   3354: 			Reply($client, "refused\n", $userinput);
                   3355: 		     
                   3356: 		    }
                   3357: # ----------------------------------------------------------------- setannounce
                   3358: 		} elsif ($userinput =~ /^setannounce/) {
                   3359: 		    if (isClient) {
                   3360: 			my ($cmd,$announcement)=split(/:/,$userinput);
                   3361: 			chomp($announcement);
                   3362: 			$announcement=&unescape($announcement);
                   3363: 			if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}.
                   3364: 						    '/announcement.txt')) {
                   3365: 			    print $store $announcement;
                   3366: 			    close $store;
                   3367: 			    print $client "ok\n";
                   3368: 			} else {
                   3369: 			    print $client "error: ".($!+0)."\n";
                   3370: 			}
                   3371: 		    } else {
                   3372: 			Reply($client, "refused\n", $userinput);
                   3373: 		       
                   3374: 		    }
                   3375: # ------------------------------------------------------------------ Hanging up
                   3376: 		} elsif (($userinput =~ /^exit/) ||
                   3377: 			 ($userinput =~ /^init/)) { # no restrictions.
                   3378: 		    &logthis(
                   3379: 			     "Client $clientip ($clientname) hanging up: $userinput");
                   3380: 		    print $client "bye\n";
                   3381: 		    $client->shutdown(2);        # shutdown the socket forcibly.
                   3382: 		    $client->close();
                   3383: 		    last;
1.161     foxr     3384: 
1.178     foxr     3385: # ---------------------------------- set current host/domain
                   3386: 		} elsif ($userinput =~ /^sethost:/) {
                   3387: 		    if (isClient) {
                   3388: 			print $client &sethost($userinput)."\n";
                   3389: 		    } else {
                   3390: 			print $client "refused\n";
                   3391: 		    }
                   3392: #---------------------------------- request file (?) version.
                   3393: 		} elsif ($userinput =~/^version:/) {
                   3394: 		    if (isClient) {
                   3395: 			print $client &version($userinput)."\n";
                   3396: 		    } else {
                   3397: 			print $client "refused\n";
                   3398: 		    }
1.193     raeburn  3399: #------------------------------- is auto-enrollment enabled?
1.200     matthew  3400:                 } elsif ($userinput =~/^autorun:/) {
1.193     raeburn  3401:                     if (isClient) {
1.200     matthew  3402:                         my ($cmd,$cdom) = split(/:/,$userinput);
                   3403:                         my $outcome = &localenroll::run($cdom);
1.193     raeburn  3404:                         print $client "$outcome\n";
                   3405:                     } else {
                   3406:                         print $client "0\n";
                   3407:                     }
                   3408: #------------------------------- get official sections (for auto-enrollment).
1.200     matthew  3409:                 } elsif ($userinput =~/^autogetsections:/) {
1.193     raeburn  3410:                     if (isClient) {
1.200     matthew  3411:                         my ($cmd,$coursecode,$cdom)=split(/:/,$userinput);
                   3412:                         my @secs = &localenroll::get_sections($coursecode,$cdom);
1.193     raeburn  3413:                         my $seclist = &escape(join(':',@secs));
                   3414:                         print $client "$seclist\n";
                   3415:                     } else {
                   3416:                         print $client "refused\n";
                   3417:                     }
                   3418: #----------------------- validate owner of new course section (for auto-enrollment).
1.200     matthew  3419:                 } elsif ($userinput =~/^autonewcourse:/) {
1.193     raeburn  3420:                     if (isClient) {
1.200     matthew  3421:                         my ($cmd,$inst_course_id,$owner,$cdom)=split(/:/,$userinput);
                   3422:                         my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom);
1.193     raeburn  3423:                         print $client "$outcome\n";
                   3424:                     } else {
                   3425:                         print $client "refused\n";
                   3426:                     }
                   3427: #-------------- validate course section in schedule of classes (for auto-enrollment).
1.200     matthew  3428:                 } elsif ($userinput =~/^autovalidatecourse:/) {
1.193     raeburn  3429:                     if (isClient) {
1.200     matthew  3430:                         my ($cmd,$inst_course_id,$cdom)=split(/:/,$userinput);
                   3431:                         my $outcome=&localenroll::validate_courseID($inst_course_id,$cdom);
1.193     raeburn  3432:                         print $client "$outcome\n";
                   3433:                     } else {
                   3434:                         print $client "refused\n";
                   3435:                     }
                   3436: #--------------------------- create password for new user (for auto-enrollment).
1.200     matthew  3437:                 } elsif ($userinput =~/^autocreatepassword:/) {
1.193     raeburn  3438:                     if (isClient) {
1.200     matthew  3439:                         my ($cmd,$authparam,$cdom)=split(/:/,$userinput);
                   3440:                         my ($create_passwd,$authchk);
                   3441:                         ($authparam,$create_passwd,$authchk) = &localenroll::create_password($authparam,$cdom);
1.193     raeburn  3442:                         print $client &escape($authparam.':'.$create_passwd.':'.$authchk)."\n";
                   3443:                     } else {
                   3444:                         print $client "refused\n";
                   3445:                     }
                   3446: #---------------------------  read and remove temporary files (for auto-enrollment).
1.200     matthew  3447:                 } elsif ($userinput =~/^autoretrieve:/) {
1.193     raeburn  3448:                     if (isClient) {
                   3449:                         my ($cmd,$filename) = split(/:/,$userinput);
                   3450:                         my $source = $perlvar{'lonDaemons'}.'/tmp/'.$filename;
                   3451:                         if ( (-e $source) && ($filename ne '') ) {
                   3452:                             my $reply = '';
                   3453:                             if (open(my $fh,$source)) {
                   3454:                                 while (<$fh>) {
                   3455:                                     chomp($_);
                   3456:                                     $_ =~ s/^\s+//g;
                   3457:                                     $_ =~ s/\s+$//g;
                   3458:                                     $reply .= $_;
                   3459:                                 }
                   3460:                                 close($fh);
                   3461:                                 print $client &escape($reply)."\n";
                   3462: #                                unlink($source);
                   3463:                             } else {
                   3464:                                 print $client "error\n";
                   3465:                             }
                   3466:                         } else {
                   3467:                             print $client "error\n";
                   3468:                         }
                   3469:                     } else {
                   3470:                         print $client "refused\n";
                   3471:                     }
1.205     raeburn  3472: #---------------------  read and retrieve institutional code format (for support form).
                   3473:                 } elsif ($userinput =~/^autoinstcodeformat:/) {
                   3474:                     if (isClient) {
                   3475:                         my $reply;
                   3476:                         my($cmd,$cdom,$course) = split(/:/,$userinput);
                   3477:                         my @pairs = split/\&/,$course;
                   3478:                         my %instcodes = ();
                   3479:                         my %codes = ();
                   3480:                         my @codetitles = ();
                   3481:                         my %cat_titles = ();
                   3482:                         my %cat_order = ();
                   3483:                         foreach (@pairs) {
                   3484:                             my ($key,$value) = split/=/,$_;
                   3485:                             $instcodes{&unescape($key)} = &unescape($value);
                   3486:                         }
                   3487:                         my $formatreply = &localenroll::instcode_format($cdom,\%instcodes,\%codes,\@codetitles,\%cat_titles,\%cat_order);
                   3488:                         if ($formatreply eq 'ok') {
                   3489:                             my $codes_str = &hash2str(%codes);
                   3490:                             my $codetitles_str = &array2str(@codetitles);
                   3491:                             my $cat_titles_str = &hash2str(%cat_titles);
                   3492:                             my $cat_order_str = &hash2str(%cat_order);
                   3493:                             print $client $codes_str.':'.$codetitles_str.':'.$cat_titles_str.':'.$cat_order_str."\n";
                   3494:                         }
                   3495:                     } else {
                   3496:                         print $client "refused\n";
                   3497:                     }
1.178     foxr     3498: # ------------------------------------------------------------- unknown command
1.161     foxr     3499: 
1.178     foxr     3500: 		} else {
                   3501: 		    # unknown command
                   3502: 		    print $client "unknown_cmd\n";
                   3503: 		}
1.177     foxr     3504: # -------------------------------------------------------------------- complete
1.178     foxr     3505: 		alarm(0);
1.200     matthew  3506: 		&status('Listening to '.$clientname." ($keymode)");
1.161     foxr     3507: 	    }
1.59      www      3508: # --------------------------------------------- client unknown or fishy, refuse
1.161     foxr     3509: 	} else {
                   3510: 	    print $client "refused\n";
                   3511: 	    $client->close();
1.190     albertel 3512: 	    &logthis("<font color='blue'>WARNING: "
1.161     foxr     3513: 		     ."Rejected client $clientip, closing connection</font>");
                   3514: 	}
                   3515:     }             
                   3516:     
1.1       albertel 3517: # =============================================================================
1.161     foxr     3518:     
1.190     albertel 3519:     &logthis("<font color='red'>CRITICAL: "
1.161     foxr     3520: 	     ."Disconnect from $clientip ($clientname)</font>");    
                   3521:     
                   3522:     
                   3523:     # this exit is VERY important, otherwise the child will become
                   3524:     # a producer of more and more children, forking yourself into
                   3525:     # process death.
                   3526:     exit;
1.106     foxr     3527:     
1.78      foxr     3528: }
                   3529: 
                   3530: 
                   3531: #
                   3532: #   Checks to see if the input roleput request was to set
                   3533: # an author role.  If so, invokes the lchtmldir script to set
                   3534: # up a correct public_html 
                   3535: # Parameters:
                   3536: #    request   - The request sent to the rolesput subchunk.
                   3537: #                We're looking for  /domain/_au
                   3538: #    domain    - The domain in which the user is having roles doctored.
                   3539: #    user      - Name of the user for which the role is being put.
                   3540: #    authtype  - The authentication type associated with the user.
                   3541: #
                   3542: sub ManagePermissions
                   3543: {
1.192     foxr     3544: 
                   3545:     my ($request, $domain, $user, $authtype) = @_;
1.78      foxr     3546: 
                   3547:     # See if the request is of the form /$domain/_au
                   3548:     if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput...
                   3549: 	my $execdir = $perlvar{'lonDaemons'};
                   3550: 	my $userhome= "/home/$user" ;
1.134     albertel 3551: 	&logthis("system $execdir/lchtmldir $userhome $user $authtype");
1.78      foxr     3552: 	system("$execdir/lchtmldir $userhome $user $authtype");
                   3553:     }
                   3554: }
                   3555: #
                   3556: #   GetAuthType - Determines the authorization type of a user in a domain.
                   3557: 
                   3558: #     Returns the authorization type or nouser if there is no such user.
                   3559: #
                   3560: sub GetAuthType 
                   3561: {
1.192     foxr     3562: 
                   3563:     my ($domain, $user)  = @_;
1.78      foxr     3564: 
1.79      foxr     3565:     Debug("GetAuthType( $domain, $user ) \n");
1.78      foxr     3566:     my $proname    = &propath($domain, $user); 
                   3567:     my $passwdfile = "$proname/passwd";
                   3568:     if( -e $passwdfile ) {
                   3569: 	my $pf = IO::File->new($passwdfile);
                   3570: 	my $realpassword = <$pf>;
                   3571: 	chomp($realpassword);
1.79      foxr     3572: 	Debug("Password info = $realpassword\n");
1.78      foxr     3573: 	my ($authtype, $contentpwd) = split(/:/, $realpassword);
1.79      foxr     3574: 	Debug("Authtype = $authtype, content = $contentpwd\n");
1.78      foxr     3575: 	my $availinfo = '';
1.91      albertel 3576: 	if($authtype eq 'krb4' or $authtype eq 'krb5') {
1.78      foxr     3577: 	    $availinfo = $contentpwd;
                   3578: 	}
1.79      foxr     3579: 
1.78      foxr     3580: 	return "$authtype:$availinfo";
                   3581:     }
                   3582:     else {
1.79      foxr     3583: 	Debug("Returning nouser");
1.78      foxr     3584: 	return "nouser";
                   3585:     }
1.1       albertel 3586: }
                   3587: 
1.84      albertel 3588: sub addline {
                   3589:     my ($fname,$hostid,$ip,$newline)=@_;
                   3590:     my $contents;
                   3591:     my $found=0;
                   3592:     my $expr='^'.$hostid.':'.$ip.':';
                   3593:     $expr =~ s/\./\\\./g;
1.134     albertel 3594:     my $sh;
1.84      albertel 3595:     if ($sh=IO::File->new("$fname.subscription")) {
                   3596: 	while (my $subline=<$sh>) {
                   3597: 	    if ($subline !~ /$expr/) {$contents.= $subline;} else {$found=1;}
                   3598: 	}
                   3599: 	$sh->close();
                   3600:     }
                   3601:     $sh=IO::File->new(">$fname.subscription");
                   3602:     if ($contents) { print $sh $contents; }
                   3603:     if ($newline) { print $sh $newline; }
                   3604:     $sh->close();
                   3605:     return $found;
1.86      www      3606: }
                   3607: 
                   3608: sub getchat {
1.122     www      3609:     my ($cdom,$cname,$udom,$uname)=@_;
1.87      www      3610:     my %hash;
                   3611:     my $proname=&propath($cdom,$cname);
                   3612:     my @entries=();
1.88      albertel 3613:     if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
                   3614: 	    &GDBM_READER(),0640)) {
                   3615: 	@entries=map { $_.':'.$hash{$_} } sort keys %hash;
                   3616: 	untie %hash;
1.123     www      3617:     }
1.124     www      3618:     my @participants=();
1.134     albertel 3619:     my $cutoff=time-60;
1.123     www      3620:     if (tie(%hash,'GDBM_File',"$proname/nohist_inchatroom.db",
1.124     www      3621: 	    &GDBM_WRCREAT(),0640)) {
                   3622:         $hash{$uname.':'.$udom}=time;
1.123     www      3623:         foreach (sort keys %hash) {
                   3624: 	    if ($hash{$_}>$cutoff) {
1.124     www      3625: 		$participants[$#participants+1]='active_participant:'.$_;
1.123     www      3626:             }
                   3627:         }
                   3628:         untie %hash;
1.86      www      3629:     }
1.124     www      3630:     return (@participants,@entries);
1.86      www      3631: }
                   3632: 
                   3633: sub chatadd {
1.88      albertel 3634:     my ($cdom,$cname,$newchat)=@_;
                   3635:     my %hash;
                   3636:     my $proname=&propath($cdom,$cname);
                   3637:     my @entries=();
1.142     www      3638:     my $time=time;
1.88      albertel 3639:     if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
                   3640: 	    &GDBM_WRCREAT(),0640)) {
                   3641: 	@entries=map { $_.':'.$hash{$_} } sort keys %hash;
                   3642: 	my ($lastid)=($entries[$#entries]=~/^(\w+)\:/);
                   3643: 	my ($thentime,$idnum)=split(/\_/,$lastid);
                   3644: 	my $newid=$time.'_000000';
                   3645: 	if ($thentime==$time) {
                   3646: 	    $idnum=~s/^0+//;
                   3647: 	    $idnum++;
                   3648: 	    $idnum=substr('000000'.$idnum,-6,6);
                   3649: 	    $newid=$time.'_'.$idnum;
                   3650: 	}
                   3651: 	$hash{$newid}=$newchat;
                   3652: 	my $expired=$time-3600;
                   3653: 	foreach (keys %hash) {
                   3654: 	    my ($thistime)=($_=~/(\d+)\_/);
                   3655: 	    if ($thistime<$expired) {
1.89      www      3656: 		delete $hash{$_};
1.88      albertel 3657: 	    }
                   3658: 	}
                   3659: 	untie %hash;
1.142     www      3660:     }
                   3661:     {
                   3662: 	my $hfh;
                   3663: 	if ($hfh=IO::File->new(">>$proname/chatroom.log")) { 
                   3664: 	    print $hfh "$time:".&unescape($newchat)."\n";
                   3665: 	}
1.86      www      3666:     }
1.84      albertel 3667: }
                   3668: 
                   3669: sub unsub {
                   3670:     my ($fname,$clientip)=@_;
                   3671:     my $result;
1.188     foxr     3672:     my $unsubs = 0;		# Number of successful unsubscribes:
                   3673: 
                   3674: 
                   3675:     # An old way subscriptions were handled was to have a 
                   3676:     # subscription marker file:
                   3677: 
                   3678:     Debug("Attempting unlink of $fname.$clientname");
1.161     foxr     3679:     if (unlink("$fname.$clientname")) {
1.188     foxr     3680: 	$unsubs++;		# Successful unsub via marker file.
                   3681:     } 
                   3682: 
                   3683:     # The more modern way to do it is to have a subscription list
                   3684:     # file:
                   3685: 
1.84      albertel 3686:     if (-e "$fname.subscription") {
1.161     foxr     3687: 	my $found=&addline($fname,$clientname,$clientip,'');
1.188     foxr     3688: 	if ($found) { 
                   3689: 	    $unsubs++;
                   3690: 	}
                   3691:     } 
                   3692: 
                   3693:     #  If either or both of these mechanisms succeeded in unsubscribing a 
                   3694:     #  resource we can return ok:
                   3695: 
                   3696:     if($unsubs) {
                   3697: 	$result = "ok\n";
1.84      albertel 3698:     } else {
1.188     foxr     3699: 	$result = "not_subscribed\n";
1.84      albertel 3700:     }
1.188     foxr     3701: 
1.84      albertel 3702:     return $result;
                   3703: }
                   3704: 
1.101     www      3705: sub currentversion {
                   3706:     my $fname=shift;
                   3707:     my $version=-1;
                   3708:     my $ulsdir='';
                   3709:     if ($fname=~/^(.+)\/[^\/]+$/) {
                   3710:        $ulsdir=$1;
                   3711:     }
1.114     albertel 3712:     my ($fnamere1,$fnamere2);
                   3713:     # remove version if already specified
1.101     www      3714:     $fname=~s/\.\d+\.(\w+(?:\.meta)*)$/\.$1/;
1.114     albertel 3715:     # get the bits that go before and after the version number
                   3716:     if ( $fname=~/^(.*\.)(\w+(?:\.meta)*)$/ ) {
                   3717: 	$fnamere1=$1;
                   3718: 	$fnamere2='.'.$2;
                   3719:     }
1.101     www      3720:     if (-e $fname) { $version=1; }
                   3721:     if (-e $ulsdir) {
1.134     albertel 3722: 	if(-d $ulsdir) {
                   3723: 	    if (opendir(LSDIR,$ulsdir)) {
                   3724: 		my $ulsfn;
                   3725: 		while ($ulsfn=readdir(LSDIR)) {
1.101     www      3726: # see if this is a regular file (ignore links produced earlier)
1.134     albertel 3727: 		    my $thisfile=$ulsdir.'/'.$ulsfn;
                   3728: 		    unless (-l $thisfile) {
1.160     www      3729: 			if ($thisfile=~/\Q$fnamere1\E(\d+)\Q$fnamere2\E$/) {
1.134     albertel 3730: 			    if ($1>$version) { $version=$1; }
                   3731: 			}
                   3732: 		    }
                   3733: 		}
                   3734: 		closedir(LSDIR);
                   3735: 		$version++;
                   3736: 	    }
                   3737: 	}
                   3738:     }
                   3739:     return $version;
1.101     www      3740: }
                   3741: 
                   3742: sub thisversion {
                   3743:     my $fname=shift;
                   3744:     my $version=-1;
                   3745:     if ($fname=~/\.(\d+)\.\w+(?:\.meta)*$/) {
                   3746: 	$version=$1;
                   3747:     }
                   3748:     return $version;
                   3749: }
                   3750: 
1.84      albertel 3751: sub subscribe {
                   3752:     my ($userinput,$clientip)=@_;
                   3753:     my $result;
                   3754:     my ($cmd,$fname)=split(/:/,$userinput);
                   3755:     my $ownership=&ishome($fname);
                   3756:     if ($ownership eq 'owner') {
1.101     www      3757: # explitly asking for the current version?
                   3758:         unless (-e $fname) {
                   3759:             my $currentversion=&currentversion($fname);
                   3760: 	    if (&thisversion($fname)==$currentversion) {
                   3761:                 if ($fname=~/^(.+)\.\d+\.(\w+(?:\.meta)*)$/) {
                   3762: 		    my $root=$1;
                   3763:                     my $extension=$2;
                   3764:                     symlink($root.'.'.$extension,
                   3765:                             $root.'.'.$currentversion.'.'.$extension);
1.102     www      3766:                     unless ($extension=~/\.meta$/) {
                   3767:                        symlink($root.'.'.$extension.'.meta',
                   3768:                             $root.'.'.$currentversion.'.'.$extension.'.meta');
                   3769: 		    }
1.101     www      3770:                 }
                   3771:             }
                   3772:         }
1.84      albertel 3773: 	if (-e $fname) {
                   3774: 	    if (-d $fname) {
                   3775: 		$result="directory\n";
                   3776: 	    } else {
1.161     foxr     3777: 		if (-e "$fname.$clientname") {&unsub($fname,$clientip);}
1.134     albertel 3778: 		my $now=time;
1.161     foxr     3779: 		my $found=&addline($fname,$clientname,$clientip,
                   3780: 				   "$clientname:$clientip:$now\n");
1.84      albertel 3781: 		if ($found) { $result="$fname\n"; }
                   3782: 		# if they were subscribed to only meta data, delete that
                   3783:                 # subscription, when you subscribe to a file you also get
                   3784:                 # the metadata
                   3785: 		unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); }
                   3786: 		$fname=~s/\/home\/httpd\/html\/res/raw/;
                   3787: 		$fname="http://$thisserver/".$fname;
                   3788: 		$result="$fname\n";
                   3789: 	    }
                   3790: 	} else {
                   3791: 	    $result="not_found\n";
                   3792: 	}
                   3793:     } else {
                   3794: 	$result="rejected\n";
                   3795:     }
                   3796:     return $result;
                   3797: }
1.91      albertel 3798: 
                   3799: sub make_passwd_file {
1.98      foxr     3800:     my ($uname, $umode,$npass,$passfilename)=@_;
1.91      albertel 3801:     my $result="ok\n";
                   3802:     if ($umode eq 'krb4' or $umode eq 'krb5') {
                   3803: 	{
                   3804: 	    my $pf = IO::File->new(">$passfilename");
                   3805: 	    print $pf "$umode:$npass\n";
                   3806: 	}
                   3807:     } elsif ($umode eq 'internal') {
                   3808: 	my $salt=time;
                   3809: 	$salt=substr($salt,6,2);
                   3810: 	my $ncpass=crypt($npass,$salt);
                   3811: 	{
                   3812: 	    &Debug("Creating internal auth");
                   3813: 	    my $pf = IO::File->new(">$passfilename");
                   3814: 	    print $pf "internal:$ncpass\n"; 
                   3815: 	}
                   3816:     } elsif ($umode eq 'localauth') {
                   3817: 	{
                   3818: 	    my $pf = IO::File->new(">$passfilename");
                   3819: 	    print $pf "localauth:$npass\n";
                   3820: 	}
                   3821:     } elsif ($umode eq 'unix') {
                   3822: 	{
1.186     foxr     3823: 	    #
                   3824: 	    #  Don't allow the creation of privileged accounts!!! that would
                   3825: 	    #  be real bad!!!
                   3826: 	    #
                   3827: 	    my $uid = getpwnam($uname);
                   3828: 	    if((defined $uid) && ($uid == 0)) {
                   3829: 		&logthis(">>>Attempted to create privilged account blocked");
                   3830: 		return "no_priv_account_error\n";
                   3831: 	    }
                   3832: 
1.91      albertel 3833: 	    my $execpath="$perlvar{'lonDaemons'}/"."lcuseradd";
                   3834: 	    {
                   3835: 		&Debug("Executing external: ".$execpath);
1.98      foxr     3836: 		&Debug("user  = ".$uname.", Password =". $npass);
1.132     matthew  3837: 		my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log");
1.91      albertel 3838: 		print $se "$uname\n";
                   3839: 		print $se "$npass\n";
                   3840: 		print $se "$npass\n";
1.97      foxr     3841: 	    }
                   3842: 	    my $useraddok = $?;
                   3843: 	    if($useraddok > 0) {
                   3844: 		&logthis("Failed lcuseradd: ".&lcuseraddstrerror($useraddok));
1.91      albertel 3845: 	    }
                   3846: 	    my $pf = IO::File->new(">$passfilename");
                   3847: 	    print $pf "unix:\n";
                   3848: 	}
                   3849:     } elsif ($umode eq 'none') {
                   3850: 	{
                   3851: 	    my $pf = IO::File->new(">$passfilename");
                   3852: 	    print $pf "none:\n";
                   3853: 	}
                   3854:     } else {
                   3855: 	$result="auth_mode_error\n";
                   3856:     }
                   3857:     return $result;
1.121     albertel 3858: }
                   3859: 
                   3860: sub sethost {
                   3861:     my ($remotereq) = @_;
                   3862:     my (undef,$hostid)=split(/:/,$remotereq);
                   3863:     if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; }
                   3864:     if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) {
1.200     matthew  3865: 	$currenthostid  =$hostid;
1.121     albertel 3866: 	$currentdomainid=$hostdom{$hostid};
                   3867: 	&logthis("Setting hostid to $hostid, and domain to $currentdomainid");
                   3868:     } else {
                   3869: 	&logthis("Requested host id $hostid not an alias of ".
                   3870: 		 $perlvar{'lonHostID'}." refusing connection");
                   3871: 	return 'unable_to_set';
                   3872:     }
                   3873:     return 'ok';
                   3874: }
                   3875: 
                   3876: sub version {
                   3877:     my ($userinput)=@_;
                   3878:     $remoteVERSION=(split(/:/,$userinput))[1];
                   3879:     return "version:$VERSION";
1.127     albertel 3880: }
1.178     foxr     3881: 
1.128     albertel 3882: #There is a copy of this in lonnet.pm
1.127     albertel 3883: sub userload {
                   3884:     my $numusers=0;
                   3885:     {
                   3886: 	opendir(LONIDS,$perlvar{'lonIDsDir'});
                   3887: 	my $filename;
                   3888: 	my $curtime=time;
                   3889: 	while ($filename=readdir(LONIDS)) {
                   3890: 	    if ($filename eq '.' || $filename eq '..') {next;}
1.138     albertel 3891: 	    my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
1.159     albertel 3892: 	    if ($curtime-$mtime < 1800) { $numusers++; }
1.127     albertel 3893: 	}
                   3894: 	closedir(LONIDS);
                   3895:     }
                   3896:     my $userloadpercent=0;
                   3897:     my $maxuserload=$perlvar{'lonUserLoadLim'};
                   3898:     if ($maxuserload) {
1.129     albertel 3899: 	$userloadpercent=100*$numusers/$maxuserload;
1.127     albertel 3900:     }
1.130     albertel 3901:     $userloadpercent=sprintf("%.2f",$userloadpercent);
1.127     albertel 3902:     return $userloadpercent;
1.91      albertel 3903: }
                   3904: 
1.205     raeburn  3905: # Routines for serializing arrays and hashes (copies from lonnet)
                   3906: 
                   3907: sub array2str {
                   3908:   my (@array) = @_;
                   3909:   my $result=&arrayref2str(\@array);
                   3910:   $result=~s/^__ARRAY_REF__//;
                   3911:   $result=~s/__END_ARRAY_REF__$//;
                   3912:   return $result;
                   3913: }
                   3914:                                                                                  
                   3915: sub arrayref2str {
                   3916:   my ($arrayref) = @_;
                   3917:   my $result='__ARRAY_REF__';
                   3918:   foreach my $elem (@$arrayref) {
                   3919:     if(ref($elem) eq 'ARRAY') {
                   3920:       $result.=&arrayref2str($elem).'&';
                   3921:     } elsif(ref($elem) eq 'HASH') {
                   3922:       $result.=&hashref2str($elem).'&';
                   3923:     } elsif(ref($elem)) {
                   3924:       #print("Got a ref of ".(ref($elem))." skipping.");
                   3925:     } else {
                   3926:       $result.=&escape($elem).'&';
                   3927:     }
                   3928:   }
                   3929:   $result=~s/\&$//;
                   3930:   $result .= '__END_ARRAY_REF__';
                   3931:   return $result;
                   3932: }
                   3933:                                                                                  
                   3934: sub hash2str {
                   3935:   my (%hash) = @_;
                   3936:   my $result=&hashref2str(\%hash);
                   3937:   $result=~s/^__HASH_REF__//;
                   3938:   $result=~s/__END_HASH_REF__$//;
                   3939:   return $result;
                   3940: }
                   3941:                                                                                  
                   3942: sub hashref2str {
                   3943:   my ($hashref)=@_;
                   3944:   my $result='__HASH_REF__';
                   3945:   foreach (sort(keys(%$hashref))) {
                   3946:     if (ref($_) eq 'ARRAY') {
                   3947:       $result.=&arrayref2str($_).'=';
                   3948:     } elsif (ref($_) eq 'HASH') {
                   3949:       $result.=&hashref2str($_).'=';
                   3950:     } elsif (ref($_)) {
                   3951:       $result.='=';
                   3952:       #print("Got a ref of ".(ref($_))." skipping.");
                   3953:     } else {
                   3954:         if ($_) {$result.=&escape($_).'=';} else { last; }
                   3955:     }
                   3956: 
                   3957:     if(ref($hashref->{$_}) eq 'ARRAY') {
                   3958:       $result.=&arrayref2str($hashref->{$_}).'&';
                   3959:     } elsif(ref($hashref->{$_}) eq 'HASH') {
                   3960:       $result.=&hashref2str($hashref->{$_}).'&';
                   3961:     } elsif(ref($hashref->{$_})) {
                   3962:        $result.='&';
                   3963:       #print("Got a ref of ".(ref($hashref->{$_}))." skipping.");
                   3964:     } else {
                   3965:       $result.=&escape($hashref->{$_}).'&';
                   3966:     }
                   3967:   }
                   3968:   $result=~s/\&$//;
                   3969:   $result .= '__END_HASH_REF__';
                   3970:   return $result;
                   3971: }
1.200     matthew  3972: 
1.61      harris41 3973: # ----------------------------------- POD (plain old documentation, CPAN style)
                   3974: 
                   3975: =head1 NAME
                   3976: 
                   3977: lond - "LON Daemon" Server (port "LOND" 5663)
                   3978: 
                   3979: =head1 SYNOPSIS
                   3980: 
1.74      harris41 3981: Usage: B<lond>
                   3982: 
                   3983: Should only be run as user=www.  This is a command-line script which
                   3984: is invoked by B<loncron>.  There is no expectation that a typical user
                   3985: will manually start B<lond> from the command-line.  (In other words,
                   3986: DO NOT START B<lond> YOURSELF.)
1.61      harris41 3987: 
                   3988: =head1 DESCRIPTION
                   3989: 
1.74      harris41 3990: There are two characteristics associated with the running of B<lond>,
                   3991: PROCESS MANAGEMENT (starting, stopping, handling child processes)
                   3992: and SERVER-SIDE ACTIVITIES (password authentication, user creation,
                   3993: subscriptions, etc).  These are described in two large
                   3994: sections below.
                   3995: 
                   3996: B<PROCESS MANAGEMENT>
                   3997: 
1.61      harris41 3998: Preforker - server who forks first. Runs as a daemon. HUPs.
                   3999: Uses IDEA encryption
                   4000: 
1.74      harris41 4001: B<lond> forks off children processes that correspond to the other servers
                   4002: in the network.  Management of these processes can be done at the
                   4003: parent process level or the child process level.
                   4004: 
                   4005: B<logs/lond.log> is the location of log messages.
                   4006: 
                   4007: The process management is now explained in terms of linux shell commands,
                   4008: subroutines internal to this code, and signal assignments:
                   4009: 
                   4010: =over 4
                   4011: 
                   4012: =item *
                   4013: 
                   4014: PID is stored in B<logs/lond.pid>
                   4015: 
                   4016: This is the process id number of the parent B<lond> process.
                   4017: 
                   4018: =item *
                   4019: 
                   4020: SIGTERM and SIGINT
                   4021: 
                   4022: Parent signal assignment:
                   4023:  $SIG{INT}  = $SIG{TERM} = \&HUNTSMAN;
                   4024: 
                   4025: Child signal assignment:
                   4026:  $SIG{INT}  = 'DEFAULT'; (and SIGTERM is DEFAULT also)
                   4027: (The child dies and a SIGALRM is sent to parent, awaking parent from slumber
                   4028:  to restart a new child.)
                   4029: 
                   4030: Command-line invocations:
                   4031:  B<kill> B<-s> SIGTERM I<PID>
                   4032:  B<kill> B<-s> SIGINT I<PID>
                   4033: 
                   4034: Subroutine B<HUNTSMAN>:
                   4035:  This is only invoked for the B<lond> parent I<PID>.
                   4036: This kills all the children, and then the parent.
                   4037: The B<lonc.pid> file is cleared.
                   4038: 
                   4039: =item *
                   4040: 
                   4041: SIGHUP
                   4042: 
                   4043: Current bug:
                   4044:  This signal can only be processed the first time
                   4045: on the parent process.  Subsequent SIGHUP signals
                   4046: have no effect.
                   4047: 
                   4048: Parent signal assignment:
                   4049:  $SIG{HUP}  = \&HUPSMAN;
                   4050: 
                   4051: Child signal assignment:
                   4052:  none (nothing happens)
                   4053: 
                   4054: Command-line invocations:
                   4055:  B<kill> B<-s> SIGHUP I<PID>
                   4056: 
                   4057: Subroutine B<HUPSMAN>:
                   4058:  This is only invoked for the B<lond> parent I<PID>,
                   4059: This kills all the children, and then the parent.
                   4060: The B<lond.pid> file is cleared.
                   4061: 
                   4062: =item *
                   4063: 
                   4064: SIGUSR1
                   4065: 
                   4066: Parent signal assignment:
                   4067:  $SIG{USR1} = \&USRMAN;
                   4068: 
                   4069: Child signal assignment:
                   4070:  $SIG{USR1}= \&logstatus;
                   4071: 
                   4072: Command-line invocations:
                   4073:  B<kill> B<-s> SIGUSR1 I<PID>
                   4074: 
                   4075: Subroutine B<USRMAN>:
                   4076:  When invoked for the B<lond> parent I<PID>,
                   4077: SIGUSR1 is sent to all the children, and the status of
                   4078: each connection is logged.
1.144     foxr     4079: 
                   4080: =item *
                   4081: 
                   4082: SIGUSR2
                   4083: 
                   4084: Parent Signal assignment:
                   4085:     $SIG{USR2} = \&UpdateHosts
                   4086: 
                   4087: Child signal assignment:
                   4088:     NONE
                   4089: 
1.74      harris41 4090: 
                   4091: =item *
                   4092: 
                   4093: SIGCHLD
                   4094: 
                   4095: Parent signal assignment:
                   4096:  $SIG{CHLD} = \&REAPER;
                   4097: 
                   4098: Child signal assignment:
                   4099:  none
                   4100: 
                   4101: Command-line invocations:
                   4102:  B<kill> B<-s> SIGCHLD I<PID>
                   4103: 
                   4104: Subroutine B<REAPER>:
                   4105:  This is only invoked for the B<lond> parent I<PID>.
                   4106: Information pertaining to the child is removed.
                   4107: The socket port is cleaned up.
                   4108: 
                   4109: =back
                   4110: 
                   4111: B<SERVER-SIDE ACTIVITIES>
                   4112: 
                   4113: Server-side information can be accepted in an encrypted or non-encrypted
                   4114: method.
                   4115: 
                   4116: =over 4
                   4117: 
                   4118: =item ping
                   4119: 
                   4120: Query a client in the hosts.tab table; "Are you there?"
                   4121: 
                   4122: =item pong
                   4123: 
                   4124: Respond to a ping query.
                   4125: 
                   4126: =item ekey
                   4127: 
                   4128: Read in encrypted key, make cipher.  Respond with a buildkey.
                   4129: 
                   4130: =item load
                   4131: 
                   4132: Respond with CPU load based on a computation upon /proc/loadavg.
                   4133: 
                   4134: =item currentauth
                   4135: 
                   4136: Reply with current authentication information (only over an
                   4137: encrypted channel).
                   4138: 
                   4139: =item auth
                   4140: 
                   4141: Only over an encrypted channel, reply as to whether a user's
                   4142: authentication information can be validated.
                   4143: 
                   4144: =item passwd
                   4145: 
                   4146: Allow for a password to be set.
                   4147: 
                   4148: =item makeuser
                   4149: 
                   4150: Make a user.
                   4151: 
                   4152: =item passwd
                   4153: 
                   4154: Allow for authentication mechanism and password to be changed.
                   4155: 
                   4156: =item home
1.61      harris41 4157: 
1.74      harris41 4158: Respond to a question "are you the home for a given user?"
                   4159: 
                   4160: =item update
                   4161: 
                   4162: Update contents of a subscribed resource.
                   4163: 
                   4164: =item unsubscribe
                   4165: 
                   4166: The server is unsubscribing from a resource.
                   4167: 
                   4168: =item subscribe
                   4169: 
                   4170: The server is subscribing to a resource.
                   4171: 
                   4172: =item log
                   4173: 
                   4174: Place in B<logs/lond.log>
                   4175: 
                   4176: =item put
                   4177: 
                   4178: stores hash in namespace
                   4179: 
                   4180: =item rolesput
                   4181: 
                   4182: put a role into a user's environment
                   4183: 
                   4184: =item get
                   4185: 
                   4186: returns hash with keys from array
                   4187: reference filled in from namespace
                   4188: 
                   4189: =item eget
                   4190: 
                   4191: returns hash with keys from array
                   4192: reference filled in from namesp (encrypts the return communication)
                   4193: 
                   4194: =item rolesget
                   4195: 
                   4196: get a role from a user's environment
                   4197: 
                   4198: =item del
                   4199: 
                   4200: deletes keys out of array from namespace
                   4201: 
                   4202: =item keys
                   4203: 
                   4204: returns namespace keys
                   4205: 
                   4206: =item dump
                   4207: 
                   4208: dumps the complete (or key matching regexp) namespace into a hash
                   4209: 
                   4210: =item store
                   4211: 
                   4212: stores hash permanently
                   4213: for this url; hashref needs to be given and should be a \%hashname; the
                   4214: remaining args aren't required and if they aren't passed or are '' they will
                   4215: be derived from the ENV
                   4216: 
                   4217: =item restore
                   4218: 
                   4219: returns a hash for a given url
                   4220: 
                   4221: =item querysend
                   4222: 
                   4223: Tells client about the lonsql process that has been launched in response
                   4224: to a sent query.
                   4225: 
                   4226: =item queryreply
                   4227: 
                   4228: Accept information from lonsql and make appropriate storage in temporary
                   4229: file space.
                   4230: 
                   4231: =item idput
                   4232: 
                   4233: Defines usernames as corresponding to IDs.  (These "IDs" are unique identifiers
                   4234: for each student, defined perhaps by the institutional Registrar.)
                   4235: 
                   4236: =item idget
                   4237: 
                   4238: Returns usernames corresponding to IDs.  (These "IDs" are unique identifiers
                   4239: for each student, defined perhaps by the institutional Registrar.)
                   4240: 
                   4241: =item tmpput
                   4242: 
                   4243: Accept and store information in temporary space.
                   4244: 
                   4245: =item tmpget
                   4246: 
                   4247: Send along temporarily stored information.
                   4248: 
                   4249: =item ls
                   4250: 
                   4251: List part of a user's directory.
                   4252: 
1.135     foxr     4253: =item pushtable
                   4254: 
                   4255: Pushes a file in /home/httpd/lonTab directory.  Currently limited to:
                   4256: hosts.tab and domain.tab. The old file is copied to  *.tab.backup but
                   4257: must be restored manually in case of a problem with the new table file.
                   4258: pushtable requires that the request be encrypted and validated via
                   4259: ValidateManager.  The form of the command is:
                   4260: enc:pushtable tablename <tablecontents> \n
                   4261: where pushtable, tablename and <tablecontents> will be encrypted, but \n is a 
                   4262: cleartext newline.
                   4263: 
1.74      harris41 4264: =item Hanging up (exit or init)
                   4265: 
                   4266: What to do when a client tells the server that they (the client)
                   4267: are leaving the network.
                   4268: 
                   4269: =item unknown command
                   4270: 
                   4271: If B<lond> is sent an unknown command (not in the list above),
                   4272: it replys to the client "unknown_cmd".
1.135     foxr     4273: 
1.74      harris41 4274: 
                   4275: =item UNKNOWN CLIENT
                   4276: 
                   4277: If the anti-spoofing algorithm cannot verify the client,
                   4278: the client is rejected (with a "refused" message sent
                   4279: to the client, and the connection is closed.
                   4280: 
                   4281: =back
1.61      harris41 4282: 
                   4283: =head1 PREREQUISITES
                   4284: 
                   4285: IO::Socket
                   4286: IO::File
                   4287: Apache::File
                   4288: Symbol
                   4289: POSIX
                   4290: Crypt::IDEA
                   4291: LWP::UserAgent()
                   4292: GDBM_File
                   4293: Authen::Krb4
1.91      albertel 4294: Authen::Krb5
1.61      harris41 4295: 
                   4296: =head1 COREQUISITES
                   4297: 
                   4298: =head1 OSNAMES
                   4299: 
                   4300: linux
                   4301: 
                   4302: =head1 SCRIPT CATEGORIES
                   4303: 
                   4304: Server/Process
                   4305: 
                   4306: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.