Annotation of loncom/lond, revision 1.199

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