![]() ![]() | ![]() |
- better? way of cleaning up after clutter?
1: # The LearningOnline Network 2: # TCP networking package 3: # 4: # $Id: lonnet.pm,v 1.722 2006/03/27 23:00:18 albertel Exp $ 5: # 6: # Copyright Michigan State University Board of Trustees 7: # 8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 9: # 10: # LON-CAPA is free software; you can redistribute it and/or modify 11: # it under the terms of the GNU General Public License as published by 12: # the Free Software Foundation; either version 2 of the License, or 13: # (at your option) any later version. 14: # 15: # LON-CAPA is distributed in the hope that it will be useful, 16: # but WITHOUT ANY WARRANTY; without even the implied warranty of 17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18: # GNU General Public License for more details. 19: # 20: # You should have received a copy of the GNU General Public License 21: # along with LON-CAPA; if not, write to the Free Software 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23: # 24: # /home/httpd/html/adm/gpl.txt 25: # 26: # http://www.lon-capa.org/ 27: # 28: ### 29: 30: package Apache::lonnet; 31: 32: use strict; 33: use LWP::UserAgent(); 34: use HTTP::Headers; 35: use HTTP::Date; 36: # use Date::Parse; 37: use vars 38: qw(%perlvar %hostname %badServerCache %iphost %spareid %hostdom 39: %libserv %pr %prp $memcache %packagetab 40: %courselogs %accesshash %userrolehash %domainrolehash $processmarker $dumpcount 41: %coursedombuf %coursenumbuf %coursehombuf %coursedescrbuf %courseinstcodebuf %courseownerbuf 42: %domaindescription %domain_auth_def %domain_auth_arg_def 43: %domain_lang_def %domain_city %domain_longi %domain_lati %domain_primary 44: $tmpdir $_64bit %env); 45: 46: use IO::Socket; 47: use GDBM_File; 48: use Apache::Constants qw(:common :http); 49: use HTML::LCParser; 50: use HTML::Parser; 51: use Fcntl qw(:flock); 52: use Storable qw(lock_store lock_nstore lock_retrieve freeze thaw nfreeze); 53: use Time::HiRes qw( gettimeofday tv_interval ); 54: use Cache::Memcached; 55: use Digest::MD5; 56: 57: my $readit; 58: my $max_connection_retries = 10; # Or some such value. 59: 60: require Exporter; 61: 62: our @ISA = qw (Exporter); 63: our @EXPORT = qw(%env); 64: 65: =pod 66: 67: =head1 Package Variables 68: 69: These are largely undocumented, so if you decipher one please note it here. 70: 71: =over 4 72: 73: =item $processmarker 74: 75: Contains the time this process was started and this servers host id. 76: 77: =item $dumpcount 78: 79: Counts the number of times a message log flush has been attempted (regardless 80: of success) by this process. Used as part of the filename when messages are 81: delayed. 82: 83: =back 84: 85: =cut 86: 87: 88: # --------------------------------------------------------------------- Logging 89: 90: sub logtouch { 91: my $execdir=$perlvar{'lonDaemons'}; 92: unless (-e "$execdir/logs/lonnet.log") { 93: open(my $fh,">>$execdir/logs/lonnet.log"); 94: close $fh; 95: } 96: my ($wwwuid,$wwwgid)=(getpwnam('www'))[2,3]; 97: chown($wwwuid,$wwwgid,$execdir.'/logs/lonnet.log'); 98: } 99: 100: sub logthis { 101: my $message=shift; 102: my $execdir=$perlvar{'lonDaemons'}; 103: my $now=time; 104: my $local=localtime($now); 105: if (open(my $fh,">>$execdir/logs/lonnet.log")) { 106: print $fh "$local ($$): $message\n"; 107: close($fh); 108: } 109: return 1; 110: } 111: 112: sub logperm { 113: my $message=shift; 114: my $execdir=$perlvar{'lonDaemons'}; 115: my $now=time; 116: my $local=localtime($now); 117: if (open(my $fh,">>$execdir/logs/lonnet.perm.log")) { 118: print $fh "$now:$message:$local\n"; 119: close($fh); 120: } 121: return 1; 122: } 123: 124: # -------------------------------------------------- Non-critical communication 125: sub subreply { 126: my ($cmd,$server)=@_; 127: my $peerfile="$perlvar{'lonSockDir'}/".$hostname{$server}; 128: # 129: # With loncnew process trimming, there's a timing hole between lonc server 130: # process exit and the master server picking up the listen on the AF_UNIX 131: # socket. In that time interval, a lock file will exist: 132: 133: my $lockfile=$peerfile.".lock"; 134: while (-e $lockfile) { # Need to wait for the lockfile to disappear. 135: sleep(1); 136: } 137: # At this point, either a loncnew parent is listening or an old lonc 138: # or loncnew child is listening so we can connect or everything's dead. 139: # 140: # We'll give the connection a few tries before abandoning it. If 141: # connection is not possible, we'll con_lost back to the client. 142: # 143: my $client; 144: for (my $retries = 0; $retries < $max_connection_retries; $retries++) { 145: $client=IO::Socket::UNIX->new(Peer =>"$peerfile", 146: Type => SOCK_STREAM, 147: Timeout => 10); 148: if($client) { 149: last; # Connected! 150: } 151: sleep(1); # Try again later if failed connection. 152: } 153: my $answer; 154: if ($client) { 155: print $client "sethost:$server:$cmd\n"; 156: $answer=<$client>; 157: if (!$answer) { $answer="con_lost"; } 158: chomp($answer); 159: } else { 160: $answer = 'con_lost'; # Failed connection. 161: } 162: return $answer; 163: } 164: 165: sub reply { 166: my ($cmd,$server)=@_; 167: unless (defined($hostname{$server})) { return 'no_such_host'; } 168: my $answer=subreply($cmd,$server); 169: if (($answer=~/^refused/) || ($answer=~/^rejected/)) { 170: &logthis("<font color=\"blue\">WARNING:". 171: " $cmd to $server returned $answer</font>"); 172: } 173: return $answer; 174: } 175: 176: # ----------------------------------------------------------- Send USR1 to lonc 177: 178: sub reconlonc { 179: my $peerfile=shift; 180: &logthis("Trying to reconnect for $peerfile"); 181: my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid"; 182: if (open(my $fh,"<$loncfile")) { 183: my $loncpid=<$fh>; 184: chomp($loncpid); 185: if (kill 0 => $loncpid) { 186: &logthis("lonc at pid $loncpid responding, sending USR1"); 187: kill USR1 => $loncpid; 188: sleep 1; 189: if (-e "$peerfile") { return; } 190: &logthis("$peerfile still not there, give it another try"); 191: sleep 5; 192: if (-e "$peerfile") { return; } 193: &logthis( 194: "<font color=\"blue\">WARNING: $peerfile still not there, giving up</font>"); 195: } else { 196: &logthis( 197: "<font color=\"blue\">WARNING:". 198: " lonc at pid $loncpid not responding, giving up</font>"); 199: } 200: } else { 201: &logthis('<font color="blue">WARNING: lonc not running, giving up</font>'); 202: } 203: } 204: 205: # ------------------------------------------------------ Critical communication 206: 207: sub critical { 208: my ($cmd,$server)=@_; 209: unless ($hostname{$server}) { 210: &logthis("<font color=\"blue\">WARNING:". 211: " Critical message to unknown server ($server)</font>"); 212: return 'no_such_host'; 213: } 214: my $answer=reply($cmd,$server); 215: if ($answer eq 'con_lost') { 216: &reconlonc("$perlvar{'lonSockDir'}/$server"); 217: my $answer=reply($cmd,$server); 218: if ($answer eq 'con_lost') { 219: my $now=time; 220: my $middlename=$cmd; 221: $middlename=substr($middlename,0,16); 222: $middlename=~s/\W//g; 223: my $dfilename= 224: "$perlvar{'lonSockDir'}/delayed/$now.$dumpcount.$$.$middlename.$server"; 225: $dumpcount++; 226: { 227: my $dfh; 228: if (open($dfh,">$dfilename")) { 229: print $dfh "$cmd\n"; 230: close($dfh); 231: } 232: } 233: sleep 2; 234: my $wcmd=''; 235: { 236: my $dfh; 237: if (open($dfh,"<$dfilename")) { 238: $wcmd=<$dfh>; 239: close($dfh); 240: } 241: } 242: chomp($wcmd); 243: if ($wcmd eq $cmd) { 244: &logthis("<font color=\"blue\">WARNING: ". 245: "Connection buffer $dfilename: $cmd</font>"); 246: &logperm("D:$server:$cmd"); 247: return 'con_delayed'; 248: } else { 249: &logthis("<font color=\"red\">CRITICAL:" 250: ." Critical connection failed: $server $cmd</font>"); 251: &logperm("F:$server:$cmd"); 252: return 'con_failed'; 253: } 254: } 255: } 256: return $answer; 257: } 258: 259: # ------------------------------------------- Transfer profile into environment 260: 261: sub transfer_profile_to_env { 262: my ($lonidsdir,$handle)=@_; 263: if (!defined($lonidsdir)) { 264: $lonidsdir = $perlvar{'lonIDsDir'}; 265: } 266: if (!defined($handle)) { 267: ($handle) = ($env{'user.environment'} =~m|/([^/]+)\.id$| ); 268: } 269: 270: my @profile; 271: { 272: open(my $idf,"$lonidsdir/$handle.id"); 273: flock($idf,LOCK_SH); 274: @profile=<$idf>; 275: close($idf); 276: } 277: my $envi; 278: my %Remove; 279: for ($envi=0;$envi<=$#profile;$envi++) { 280: chomp($profile[$envi]); 281: my ($envname,$envvalue)=split(/=/,$profile[$envi],2); 282: $env{$envname} = $envvalue; 283: if (my ($key,$time) = ($envname =~ /^(cgi\.(\d+)_\d+\.)/)) { 284: if ($time < time-300) { 285: $Remove{$key}++; 286: } 287: } 288: } 289: $env{'user.environment'} = "$lonidsdir/$handle.id"; 290: foreach my $expired_key (keys(%Remove)) { 291: &delenv($expired_key); 292: } 293: } 294: 295: # ---------------------------------------------------------- Append Environment 296: 297: sub appenv { 298: my %newenv=@_; 299: foreach my $key (keys(%newenv)) { 300: if (($newenv{$key}=~/^user\.role/) || ($newenv{$key}=~/^user\.priv/)) { 301: &logthis("<font color=\"blue\">WARNING: ". 302: "Attempt to modify environment ".$key." to ".$newenv{$key} 303: .'</font>'); 304: delete($newenv{$key}); 305: } else { 306: $env{$key}=$newenv{$key}; 307: } 308: } 309: 310: my $lockfh; 311: unless (open($lockfh,"$env{'user.environment'}")) { 312: return 'error: '.$!; 313: } 314: unless (flock($lockfh,LOCK_EX)) { 315: &logthis("<font color=\"blue\">WARNING: ". 316: 'Could not obtain exclusive lock in appenv: '.$!); 317: close($lockfh); 318: return 'error: '.$!; 319: } 320: 321: my @oldenv; 322: { 323: my $fh; 324: unless (open($fh,"$env{'user.environment'}")) { 325: return 'error: '.$!; 326: } 327: @oldenv=<$fh>; 328: close($fh); 329: } 330: for (my $i=0; $i<=$#oldenv; $i++) { 331: chomp($oldenv[$i]); 332: if ($oldenv[$i] ne '') { 333: my ($name,$value)=split(/=/,$oldenv[$i],2); 334: unless (defined($newenv{$name})) { 335: $newenv{$name}=$value; 336: } 337: } 338: } 339: { 340: my $fh; 341: unless (open($fh,">$env{'user.environment'}")) { 342: return 'error'; 343: } 344: my $newname; 345: foreach $newname (keys %newenv) { 346: print $fh "$newname=$newenv{$newname}\n"; 347: } 348: close($fh); 349: } 350: 351: close($lockfh); 352: return 'ok'; 353: } 354: # ----------------------------------------------------- Delete from Environment 355: 356: sub delenv { 357: my $delthis=shift; 358: my %newenv=(); 359: if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) { 360: &logthis("<font color=\"blue\">WARNING: ". 361: "Attempt to delete from environment ".$delthis); 362: return 'error'; 363: } 364: my @oldenv; 365: { 366: my $fh; 367: unless (open($fh,"$env{'user.environment'}")) { 368: return 'error'; 369: } 370: unless (flock($fh,LOCK_SH)) { 371: &logthis("<font color=\"blue\">WARNING: ". 372: 'Could not obtain shared lock in delenv: '.$!); 373: close($fh); 374: return 'error: '.$!; 375: } 376: @oldenv=<$fh>; 377: close($fh); 378: } 379: { 380: my $fh; 381: unless (open($fh,">$env{'user.environment'}")) { 382: return 'error'; 383: } 384: unless (flock($fh,LOCK_EX)) { 385: &logthis("<font color=\"blue\">WARNING: ". 386: 'Could not obtain exclusive lock in delenv: '.$!); 387: close($fh); 388: return 'error: '.$!; 389: } 390: foreach my $cur_key (@oldenv) { 391: if ($cur_key=~/^$delthis/) { 392: my ($key,undef) = split('=',$cur_key,2); 393: delete($env{$key}); 394: } else { 395: print $fh $cur_key; 396: } 397: } 398: close($fh); 399: } 400: return 'ok'; 401: } 402: 403: # ------------------------------------------ Find out current server userload 404: # there is a copy in lond 405: sub userload { 406: my $numusers=0; 407: { 408: opendir(LONIDS,$perlvar{'lonIDsDir'}); 409: my $filename; 410: my $curtime=time; 411: while ($filename=readdir(LONIDS)) { 412: if ($filename eq '.' || $filename eq '..') {next;} 413: my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9]; 414: if ($curtime-$mtime < 1800) { $numusers++; } 415: } 416: closedir(LONIDS); 417: } 418: my $userloadpercent=0; 419: my $maxuserload=$perlvar{'lonUserLoadLim'}; 420: if ($maxuserload) { 421: $userloadpercent=100*$numusers/$maxuserload; 422: } 423: $userloadpercent=sprintf("%.2f",$userloadpercent); 424: return $userloadpercent; 425: } 426: 427: # ------------------------------------------ Fight off request when overloaded 428: 429: sub overloaderror { 430: my ($r,$checkserver)=@_; 431: unless ($checkserver) { $checkserver=$perlvar{'lonHostID'}; } 432: my $loadavg; 433: if ($checkserver eq $perlvar{'lonHostID'}) { 434: open(my $loadfile,'/proc/loadavg'); 435: $loadavg=<$loadfile>; 436: $loadavg =~ s/\s.*//g; 437: $loadavg = 100*$loadavg/$perlvar{'lonLoadLim'}; 438: close($loadfile); 439: } else { 440: $loadavg=&reply('load',$checkserver); 441: } 442: my $overload=$loadavg-100; 443: if ($overload>0) { 444: $r->err_headers_out->{'Retry-After'}=$overload; 445: $r->log_error('Overload of '.$overload.' on '.$checkserver); 446: return 413; 447: } 448: return ''; 449: } 450: 451: # ------------------------------ Find server with least workload from spare.tab 452: 453: sub spareserver { 454: my ($loadpercent,$userloadpercent,$want_server_name) = @_; 455: my $tryserver; 456: my $spareserver=''; 457: if ($userloadpercent !~ /\d/) { $userloadpercent=0; } 458: my $lowestserver=$loadpercent > $userloadpercent? 459: $loadpercent : $userloadpercent; 460: foreach $tryserver (keys(%spareid)) { 461: my $loadans=&reply('load',$tryserver); 462: my $userloadans=&reply('userload',$tryserver); 463: if ($loadans !~ /\d/ && $userloadans !~ /\d/) { 464: next; #didn't get a number from the server 465: } 466: my $answer; 467: if ($loadans =~ /\d/) { 468: if ($userloadans =~ /\d/) { 469: #both are numbers, pick the bigger one 470: $answer=$loadans > $userloadans? 471: $loadans : $userloadans; 472: } else { 473: $answer = $loadans; 474: } 475: } else { 476: $answer = $userloadans; 477: } 478: if (($answer =~ /\d/) && ($answer<$lowestserver)) { 479: if ($want_server_name) { 480: $spareserver=$tryserver; 481: } else { 482: $spareserver="http://$hostname{$tryserver}"; 483: } 484: $lowestserver=$answer; 485: } 486: } 487: return $spareserver; 488: } 489: 490: # --------------------------------------------- Try to change a user's password 491: 492: sub changepass { 493: my ($uname,$udom,$currentpass,$newpass,$server)=@_; 494: $currentpass = &escape($currentpass); 495: $newpass = &escape($newpass); 496: my $answer = reply("encrypt:passwd:$udom:$uname:$currentpass:$newpass", 497: $server); 498: if (! $answer) { 499: &logthis("No reply on password change request to $server ". 500: "by $uname in domain $udom."); 501: } elsif ($answer =~ "^ok") { 502: &logthis("$uname in $udom successfully changed their password ". 503: "on $server."); 504: } elsif ($answer =~ "^pwchange_failure") { 505: &logthis("$uname in $udom was unable to change their password ". 506: "on $server. The action was blocked by either lcpasswd ". 507: "or pwchange"); 508: } elsif ($answer =~ "^non_authorized") { 509: &logthis("$uname in $udom did not get their password correct when ". 510: "attempting to change it on $server."); 511: } elsif ($answer =~ "^auth_mode_error") { 512: &logthis("$uname in $udom attempted to change their password despite ". 513: "not being locally or internally authenticated on $server."); 514: } elsif ($answer =~ "^unknown_user") { 515: &logthis("$uname in $udom attempted to change their password ". 516: "on $server but were unable to because $server is not ". 517: "their home server."); 518: } elsif ($answer =~ "^refused") { 519: &logthis("$server refused to change $uname in $udom password because ". 520: "it was sent an unencrypted request to change the password."); 521: } 522: return $answer; 523: } 524: 525: # ----------------------- Try to determine user's current authentication scheme 526: 527: sub queryauthenticate { 528: my ($uname,$udom)=@_; 529: my $uhome=&homeserver($uname,$udom); 530: if (!$uhome) { 531: &logthis("User $uname at $udom is unknown when looking for authentication mechanism"); 532: return 'no_host'; 533: } 534: my $answer=reply("encrypt:currentauth:$udom:$uname",$uhome); 535: if ($answer =~ /^(unknown_user|refused|con_lost)/) { 536: &logthis("User $uname at $udom threw error $answer when checking authentication mechanism"); 537: } 538: return $answer; 539: } 540: 541: # --------- Try to authenticate user from domain's lib servers (first this one) 542: 543: sub authenticate { 544: my ($uname,$upass,$udom)=@_; 545: $upass=escape($upass); 546: $uname=~s/\W//g; 547: my $uhome=&homeserver($uname,$udom); 548: if (!$uhome) { 549: &logthis("User $uname at $udom is unknown in authenticate"); 550: return 'no_host'; 551: } 552: my $answer=reply("encrypt:auth:$udom:$uname:$upass",$uhome); 553: if ($answer eq 'authorized') { 554: &logthis("User $uname at $udom authorized by $uhome"); 555: return $uhome; 556: } 557: if ($answer eq 'non_authorized') { 558: &logthis("User $uname at $udom rejected by $uhome"); 559: return 'no_host'; 560: } 561: &logthis("User $uname at $udom threw error $answer when checking authentication mechanism"); 562: return 'no_host'; 563: } 564: 565: # ---------------------- Find the homebase for a user from domain's lib servers 566: 567: my %homecache; 568: sub homeserver { 569: my ($uname,$udom,$ignoreBadCache)=@_; 570: my $index="$uname:$udom"; 571: 572: if (exists($homecache{$index})) { return $homecache{$index}; } 573: my $tryserver; 574: foreach $tryserver (keys %libserv) { 575: next if ($ignoreBadCache ne 'true' && 576: exists($badServerCache{$tryserver})); 577: if ($hostdom{$tryserver} eq $udom) { 578: my $answer=reply("home:$udom:$uname",$tryserver); 579: if ($answer eq 'found') { 580: return $homecache{$index}=$tryserver; 581: } elsif ($answer eq 'no_host') { 582: $badServerCache{$tryserver}=1; 583: } 584: } 585: } 586: return 'no_host'; 587: } 588: 589: # ------------------------------------- Find the usernames behind a list of IDs 590: 591: sub idget { 592: my ($udom,@ids)=@_; 593: my %returnhash=(); 594: 595: my $tryserver; 596: foreach $tryserver (keys %libserv) { 597: if ($hostdom{$tryserver} eq $udom) { 598: my $idlist=join('&',@ids); 599: $idlist=~tr/A-Z/a-z/; 600: my $reply=&reply("idget:$udom:".$idlist,$tryserver); 601: my @answer=(); 602: if (($reply ne 'con_lost') && ($reply!~/^error\:/)) { 603: @answer=split(/\&/,$reply); 604: } ; 605: my $i; 606: for ($i=0;$i<=$#ids;$i++) { 607: if ($answer[$i]) { 608: $returnhash{$ids[$i]}=$answer[$i]; 609: } 610: } 611: } 612: } 613: return %returnhash; 614: } 615: 616: # ------------------------------------- Find the IDs behind a list of usernames 617: 618: sub idrget { 619: my ($udom,@unames)=@_; 620: my %returnhash=(); 621: foreach (@unames) { 622: $returnhash{$_}=(&userenvironment($udom,$_,'id'))[1]; 623: } 624: return %returnhash; 625: } 626: 627: # ------------------------------- Store away a list of names and associated IDs 628: 629: sub idput { 630: my ($udom,%ids)=@_; 631: my %servers=(); 632: foreach (keys %ids) { 633: &cput('environment',{'id'=>$ids{$_}},$udom,$_); 634: my $uhom=&homeserver($_,$udom); 635: if ($uhom ne 'no_host') { 636: my $id=&escape($ids{$_}); 637: $id=~tr/A-Z/a-z/; 638: my $unam=&escape($_); 639: if ($servers{$uhom}) { 640: $servers{$uhom}.='&'.$id.'='.$unam; 641: } else { 642: $servers{$uhom}=$id.'='.$unam; 643: } 644: } 645: } 646: foreach (keys %servers) { 647: &critical('idput:'.$udom.':'.$servers{$_},$_); 648: } 649: } 650: 651: # --------------------------------------------------- Assign a key to a student 652: 653: sub assign_access_key { 654: # 655: # a valid key looks like uname:udom#comments 656: # comments are being appended 657: # 658: my ($ckey,$kdom,$knum,$cdom,$cnum,$udom,$uname,$logentry)=@_; 659: $kdom= 660: $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($kdom)); 661: $knum= 662: $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($knum)); 663: $cdom= 664: $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom)); 665: $cnum= 666: $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum)); 667: $udom=$env{'user.name'} unless (defined($udom)); 668: $uname=$env{'user.domain'} unless (defined($uname)); 669: my %existing=&get('accesskeys',[$ckey],$kdom,$knum); 670: if (($existing{$ckey}=~/^\#(.*)$/) || # - new key 671: ($existing{$ckey}=~/^\Q$uname\E\:\Q$udom\E\#(.*)$/)) { 672: # assigned to this person 673: # - this should not happen, 674: # unless something went wrong 675: # the first time around 676: # ready to assign 677: $logentry=$1.'; '.$logentry; 678: if (&put('accesskeys',{$ckey=>$uname.':'.$udom.'#'.$logentry}, 679: $kdom,$knum) eq 'ok') { 680: # key now belongs to user 681: my $envkey='key.'.$cdom.'_'.$cnum; 682: if (&put('environment',{$envkey => $ckey}) eq 'ok') { 683: &appenv('environment.'.$envkey => $ckey); 684: return 'ok'; 685: } else { 686: return 687: 'error: Count not permanently assign key, will need to be re-entered later.'; 688: } 689: } else { 690: return 'error: Could not assign key, try again later.'; 691: } 692: } elsif (!$existing{$ckey}) { 693: # the key does not exist 694: return 'error: The key does not exist'; 695: } else { 696: # the key is somebody else's 697: return 'error: The key is already in use'; 698: } 699: } 700: 701: # ------------------------------------------ put an additional comment on a key 702: 703: sub comment_access_key { 704: # 705: # a valid key looks like uname:udom#comments 706: # comments are being appended 707: # 708: my ($ckey,$cdom,$cnum,$logentry)=@_; 709: $cdom= 710: $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom)); 711: $cnum= 712: $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum)); 713: my %existing=&get('accesskeys',[$ckey],$cdom,$cnum); 714: if ($existing{$ckey}) { 715: $existing{$ckey}.='; '.$logentry; 716: # ready to assign 717: if (&put('accesskeys',{$ckey=>$existing{$ckey}}, 718: $cdom,$cnum) eq 'ok') { 719: return 'ok'; 720: } else { 721: return 'error: Count not store comment.'; 722: } 723: } else { 724: # the key does not exist 725: return 'error: The key does not exist'; 726: } 727: } 728: 729: # ------------------------------------------------------ Generate a set of keys 730: 731: sub generate_access_keys { 732: my ($number,$cdom,$cnum,$logentry)=@_; 733: $cdom= 734: $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom)); 735: $cnum= 736: $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum)); 737: unless (&allowed('mky',$cdom)) { return 0; } 738: unless (($cdom) && ($cnum)) { return 0; } 739: if ($number>10000) { return 0; } 740: sleep(2); # make sure don't get same seed twice 741: srand(time()^($$+($$<<15))); # from "Programming Perl" 742: my $total=0; 743: for (my $i=1;$i<=$number;$i++) { 744: my $newkey=sprintf("%lx",int(100000*rand)).'-'. 745: sprintf("%lx",int(100000*rand)).'-'. 746: sprintf("%lx",int(100000*rand)); 747: $newkey=~s/1/g/g; # folks mix up 1 and l 748: $newkey=~s/0/h/g; # and also 0 and O 749: my %existing=&get('accesskeys',[$newkey],$cdom,$cnum); 750: if ($existing{$newkey}) { 751: $i--; 752: } else { 753: if (&put('accesskeys', 754: { $newkey => '# generated '.localtime(). 755: ' by '.$env{'user.name'}.'@'.$env{'user.domain'}. 756: '; '.$logentry }, 757: $cdom,$cnum) eq 'ok') { 758: $total++; 759: } 760: } 761: } 762: &log($env{'user.domain'},$env{'user.name'},$env{'user.home'}, 763: 'Generated '.$total.' keys for '.$cnum.' at '.$cdom); 764: return $total; 765: } 766: 767: # ------------------------------------------------------- Validate an accesskey 768: 769: sub validate_access_key { 770: my ($ckey,$cdom,$cnum,$udom,$uname)=@_; 771: $cdom= 772: $env{'course.'.$env{'request.course.id'}.'.domain'} unless (defined($cdom)); 773: $cnum= 774: $env{'course.'.$env{'request.course.id'}.'.num'} unless (defined($cnum)); 775: $udom=$env{'user.domain'} unless (defined($udom)); 776: $uname=$env{'user.name'} unless (defined($uname)); 777: my %existing=&get('accesskeys',[$ckey],$cdom,$cnum); 778: return ($existing{$ckey}=~/^\Q$uname\E\:\Q$udom\E\#/); 779: } 780: 781: # ------------------------------------- Find the section of student in a course 782: sub devalidate_getsection_cache { 783: my ($udom,$unam,$courseid)=@_; 784: $courseid=~s/\_/\//g; 785: $courseid=~s/^(\w)/\/$1/; 786: my $hashid="$udom:$unam:$courseid"; 787: &devalidate_cache_new('getsection',$hashid); 788: } 789: 790: sub getsection { 791: my ($udom,$unam,$courseid)=@_; 792: my $cachetime=1800; 793: $courseid=~s/\_/\//g; 794: $courseid=~s/^(\w)/\/$1/; 795: 796: my $hashid="$udom:$unam:$courseid"; 797: my ($result,$cached)=&is_cached_new('getsection',$hashid); 798: if (defined($cached)) { return $result; } 799: 800: my %Pending; 801: my %Expired; 802: # 803: # Each role can either have not started yet (pending), be active, 804: # or have expired. 805: # 806: # If there is an active role, we are done. 807: # 808: # If there is more than one role which has not started yet, 809: # choose the one which will start sooner 810: # If there is one role which has not started yet, return it. 811: # 812: # If there is more than one expired role, choose the one which ended last. 813: # If there is a role which has expired, return it. 814: # 815: foreach (split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles', 816: &homeserver($unam,$udom)))) { 817: my ($key,$value)=split(/\=/,$_); 818: $key=&unescape($key); 819: next if ($key !~/^\Q$courseid\E(?:\/)*(\w+)*\_st$/); 820: my $section=$1; 821: if ($key eq $courseid.'_st') { $section=''; } 822: my ($dummy,$end,$start)=split(/\_/,&unescape($value)); 823: my $now=time; 824: if (defined($end) && $end && ($now > $end)) { 825: $Expired{$end}=$section; 826: next; 827: } 828: if (defined($start) && $start && ($now < $start)) { 829: $Pending{$start}=$section; 830: next; 831: } 832: return &do_cache_new('getsection',$hashid,$section,$cachetime); 833: } 834: # 835: # Presumedly there will be few matching roles from the above 836: # loop and the sorting time will be negligible. 837: if (scalar(keys(%Pending))) { 838: my ($time) = sort {$a <=> $b} keys(%Pending); 839: return &do_cache_new('getsection',$hashid,$Pending{$time},$cachetime); 840: } 841: if (scalar(keys(%Expired))) { 842: my @sorted = sort {$a <=> $b} keys(%Expired); 843: my $time = pop(@sorted); 844: return &do_cache_new('getsection',$hashid,$Expired{$time},$cachetime); 845: } 846: return &do_cache_new('getsection',$hashid,'-1',$cachetime); 847: } 848: 849: sub save_cache { 850: my ($r)=@_; 851: if (! $r->is_initial_req()) { return DECLINED; } 852: &purge_remembered(); 853: #&Apache::loncommon::validate_page(); 854: undef(%env); 855: return OK; 856: } 857: 858: my $to_remember=-1; 859: my %remembered; 860: my %accessed; 861: my $kicks=0; 862: my $hits=0; 863: sub devalidate_cache_new { 864: my ($name,$id,$debug) = @_; 865: if ($debug) { &Apache::lonnet::logthis("deleting $name:$id"); } 866: $id=&escape($name.':'.$id); 867: $memcache->delete($id); 868: delete($remembered{$id}); 869: delete($accessed{$id}); 870: } 871: 872: sub is_cached_new { 873: my ($name,$id,$debug) = @_; 874: $id=&escape($name.':'.$id); 875: if (exists($remembered{$id})) { 876: if ($debug) { &Apache::lonnet::logthis("Earyl return $id of $remembered{$id} "); } 877: $accessed{$id}=[&gettimeofday()]; 878: $hits++; 879: return ($remembered{$id},1); 880: } 881: my $value = $memcache->get($id); 882: if (!(defined($value))) { 883: if ($debug) { &Apache::lonnet::logthis("getting $id is not defined"); } 884: return (undef,undef); 885: } 886: if ($value eq '__undef__') { 887: if ($debug) { &Apache::lonnet::logthis("getting $id is __undef__"); } 888: $value=undef; 889: } 890: &make_room($id,$value,$debug); 891: if ($debug) { &Apache::lonnet::logthis("getting $id is $value"); } 892: return ($value,1); 893: } 894: 895: sub do_cache_new { 896: my ($name,$id,$value,$time,$debug) = @_; 897: $id=&escape($name.':'.$id); 898: my $setvalue=$value; 899: if (!defined($setvalue)) { 900: $setvalue='__undef__'; 901: } 902: if (!defined($time) ) { 903: $time=600; 904: } 905: if ($debug) { &Apache::lonnet::logthis("Setting $id to $value"); } 906: $memcache->set($id,$setvalue,$time); 907: # need to make a copy of $value 908: #&make_room($id,$value,$debug); 909: return $value; 910: } 911: 912: sub make_room { 913: my ($id,$value,$debug)=@_; 914: $remembered{$id}=$value; 915: if ($to_remember<0) { return; } 916: $accessed{$id}=[&gettimeofday()]; 917: if (scalar(keys(%remembered)) <= $to_remember) { return; } 918: my $to_kick; 919: my $max_time=0; 920: foreach my $other (keys(%accessed)) { 921: if (&tv_interval($accessed{$other}) > $max_time) { 922: $to_kick=$other; 923: $max_time=&tv_interval($accessed{$other}); 924: } 925: } 926: delete($remembered{$to_kick}); 927: delete($accessed{$to_kick}); 928: $kicks++; 929: if ($debug) { &logthis("kicking $to_kick $max_time $kicks\n"); } 930: return; 931: } 932: 933: sub purge_remembered { 934: #&logthis("Tossing ".scalar(keys(%remembered))); 935: #&logthis(sprintf("%-20s is %s",'%remembered',length(&freeze(\%remembered)))); 936: undef(%remembered); 937: undef(%accessed); 938: } 939: # ------------------------------------- Read an entry from a user's environment 940: 941: sub userenvironment { 942: my ($udom,$unam,@what)=@_; 943: my %returnhash=(); 944: my @answer=split(/\&/, 945: &reply('get:'.$udom.':'.$unam.':environment:'.join('&',@what), 946: &homeserver($unam,$udom))); 947: my $i; 948: for ($i=0;$i<=$#what;$i++) { 949: $returnhash{$what[$i]}=&unescape($answer[$i]); 950: } 951: return %returnhash; 952: } 953: 954: # ---------------------------------------------------------- Get a studentphoto 955: sub studentphoto { 956: my ($udom,$unam,$ext) = @_; 957: my $home=&Apache::lonnet::homeserver($unam,$udom); 958: if (defined($env{'request.course.id'})) { 959: if ($env{'course.'.$env{'request.course.id'}.'.internal.showphoto'}) { 960: if ($udom eq $env{'course.'.$env{'request.course.id'}.'.domain'}) { 961: return(&retrievestudentphoto($udom,$unam,$ext)); 962: } else { 963: my ($result,$perm_reqd)= 964: &Apache::lonnet::auto_photo_permission($unam,$udom); 965: if ($result eq 'ok') { 966: if (!($perm_reqd eq 'yes')) { 967: return(&retrievestudentphoto($udom,$unam,$ext)); 968: } 969: } 970: } 971: } 972: } else { 973: my ($result,$perm_reqd) = 974: &Apache::lonnet::auto_photo_permission($unam,$udom); 975: if ($result eq 'ok') { 976: if (!($perm_reqd eq 'yes')) { 977: return(&retrievestudentphoto($udom,$unam,$ext)); 978: } 979: } 980: } 981: return '/adm/lonKaputt/lonlogo_broken.gif'; 982: } 983: 984: sub retrievestudentphoto { 985: my ($udom,$unam,$ext,$type) = @_; 986: my $home=&Apache::lonnet::homeserver($unam,$udom); 987: my $ret=&Apache::lonnet::reply("studentphoto:$udom:$unam:$ext:$type",$home); 988: if ($ret eq 'ok') { 989: my $url="/uploaded/$udom/$unam/internal/studentphoto.$ext"; 990: if ($type eq 'thumbnail') { 991: $url="/uploaded/$udom/$unam/internal/studentphoto_tn.$ext"; 992: } 993: my $tokenurl=&Apache::lonnet::tokenwrapper($url); 994: return $tokenurl; 995: } else { 996: if ($type eq 'thumbnail') { 997: return '/adm/lonKaputt/genericstudent_tn.gif'; 998: } else { 999: return '/adm/lonKaputt/lonlogo_broken.gif'; 1000: } 1001: } 1002: } 1003: 1004: # -------------------------------------------------------------------- New chat 1005: 1006: sub chatsend { 1007: my ($newentry,$anon)=@_; 1008: my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'}; 1009: my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; 1010: my $chome=$env{'course.'.$env{'request.course.id'}.'.home'}; 1011: &reply('chatsend:'.$cdom.':'.$cnum.':'. 1012: &escape($env{'user.domain'}.':'.$env{'user.name'}.':'.$anon.':'. 1013: &escape($newentry)),$chome); 1014: } 1015: 1016: # ------------------------------------------ Find current version of a resource 1017: 1018: sub getversion { 1019: my $fname=&clutter(shift); 1020: unless ($fname=~/^\/res\//) { return -1; } 1021: return ¤tversion(&filelocation('',$fname)); 1022: } 1023: 1024: sub currentversion { 1025: my $fname=shift; 1026: my ($result,$cached)=&is_cached_new('resversion',$fname); 1027: if (defined($cached)) { return $result; } 1028: my $author=$fname; 1029: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/; 1030: my ($udom,$uname)=split(/\//,$author); 1031: my $home=homeserver($uname,$udom); 1032: if ($home eq 'no_host') { 1033: return -1; 1034: } 1035: my $answer=reply("currentversion:$fname",$home); 1036: if (($answer eq 'con_lost') || ($answer eq 'rejected')) { 1037: return -1; 1038: } 1039: return &do_cache_new('resversion',$fname,$answer,600); 1040: } 1041: 1042: # ----------------------------- Subscribe to a resource, return URL if possible 1043: 1044: sub subscribe { 1045: my $fname=shift; 1046: if ($fname=~/\/(aboutme|syllabus|bulletinboard|smppg)$/) { return ''; } 1047: $fname=~s/[\n\r]//g; 1048: my $author=$fname; 1049: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/; 1050: my ($udom,$uname)=split(/\//,$author); 1051: my $home=homeserver($uname,$udom); 1052: if ($home eq 'no_host') { 1053: return 'not_found'; 1054: } 1055: my $answer=reply("sub:$fname",$home); 1056: if (($answer eq 'con_lost') || ($answer eq 'rejected')) { 1057: $answer.=' by '.$home; 1058: } 1059: return $answer; 1060: } 1061: 1062: # -------------------------------------------------------------- Replicate file 1063: 1064: sub repcopy { 1065: my $filename=shift; 1066: $filename=~s/\/+/\//g; 1067: if ($filename=~m|^/home/httpd/html/adm/|) { return 'ok'; } 1068: if ($filename=~m|^/home/httpd/html/lonUsers/|) { return 'ok'; } 1069: if ($filename=~m|^/home/httpd/html/userfiles/| or 1070: $filename=~m -^/*(uploaded|editupload)/-) { 1071: return &repcopy_userfile($filename); 1072: } 1073: $filename=~s/[\n\r]//g; 1074: my $transname="$filename.in.transfer"; 1075: if ((-e $filename) || (-e $transname)) { return 'ok'; } 1076: my $remoteurl=subscribe($filename); 1077: if ($remoteurl =~ /^con_lost by/) { 1078: &logthis("Subscribe returned $remoteurl: $filename"); 1079: return 'unavailable'; 1080: } elsif ($remoteurl eq 'not_found') { 1081: #&logthis("Subscribe returned not_found: $filename"); 1082: return 'not_found'; 1083: } elsif ($remoteurl =~ /^rejected by/) { 1084: &logthis("Subscribe returned $remoteurl: $filename"); 1085: return 'forbidden'; 1086: } elsif ($remoteurl eq 'directory') { 1087: return 'ok'; 1088: } else { 1089: my $author=$filename; 1090: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/; 1091: my ($udom,$uname)=split(/\//,$author); 1092: my $home=homeserver($uname,$udom); 1093: unless ($home eq $perlvar{'lonHostID'}) { 1094: my @parts=split(/\//,$filename); 1095: my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]"; 1096: if ($path ne "$perlvar{'lonDocRoot'}/res") { 1097: &logthis("Malconfiguration for replication: $filename"); 1098: return 'bad_request'; 1099: } 1100: my $count; 1101: for ($count=5;$count<$#parts;$count++) { 1102: $path.="/$parts[$count]"; 1103: if ((-e $path)!=1) { 1104: mkdir($path,0777); 1105: } 1106: } 1107: my $ua=new LWP::UserAgent; 1108: my $request=new HTTP::Request('GET',"$remoteurl"); 1109: my $response=$ua->request($request,$transname); 1110: if ($response->is_error()) { 1111: unlink($transname); 1112: my $message=$response->status_line; 1113: &logthis("<font color=\"blue\">WARNING:" 1114: ." LWP get: $message: $filename</font>"); 1115: return 'unavailable'; 1116: } else { 1117: if ($remoteurl!~/\.meta$/) { 1118: my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta'); 1119: my $mresponse=$ua->request($mrequest,$filename.'.meta'); 1120: if ($mresponse->is_error()) { 1121: unlink($filename.'.meta'); 1122: &logthis( 1123: "<font color=\"yellow\">INFO: No metadata: $filename</font>"); 1124: } 1125: } 1126: rename($transname,$filename); 1127: return 'ok'; 1128: } 1129: } 1130: } 1131: } 1132: 1133: # ------------------------------------------------ Get server side include body 1134: sub ssi_body { 1135: my ($filelink,%form)=@_; 1136: if (! exists($form{'LONCAPA_INTERNAL_no_discussion'})) { 1137: $form{'LONCAPA_INTERNAL_no_discussion'}='true'; 1138: } 1139: my $output=($filelink=~/^http\:/?&externalssi($filelink): 1140: &ssi($filelink,%form)); 1141: $output=~s|//(\s*<!--)? BEGIN LON-CAPA Internal.+// END LON-CAPA Internal\s*(-->)?\s||gs; 1142: $output=~s/^.*?\<body[^\>]*\>//si; 1143: $output=~s/(.*)\<\/body\s*\>.*?$/$1/si; 1144: return $output; 1145: } 1146: 1147: # --------------------------------------------------------- Server Side Include 1148: 1149: sub ssi { 1150: 1151: my ($fn,%form)=@_; 1152: 1153: my $ua=new LWP::UserAgent; 1154: 1155: my $request; 1156: 1157: $form{'no_update_last_known'}=1; 1158: 1159: if (%form) { 1160: $request=new HTTP::Request('POST',"http://".$ENV{'HTTP_HOST'}.$fn); 1161: $request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys %form)); 1162: } else { 1163: $request=new HTTP::Request('GET',"http://".$ENV{'HTTP_HOST'}.$fn); 1164: } 1165: 1166: $request->header(Cookie => $ENV{'HTTP_COOKIE'}); 1167: my $response=$ua->request($request); 1168: 1169: return $response->content; 1170: } 1171: 1172: sub externalssi { 1173: my ($url)=@_; 1174: my $ua=new LWP::UserAgent; 1175: my $request=new HTTP::Request('GET',$url); 1176: my $response=$ua->request($request); 1177: return $response->content; 1178: } 1179: 1180: # -------------------------------- Allow a /uploaded/ URI to be vouched for 1181: 1182: sub allowuploaded { 1183: my ($srcurl,$url)=@_; 1184: $url=&clutter(&declutter($url)); 1185: my $dir=$url; 1186: $dir=~s/\/[^\/]+$//; 1187: my %httpref=(); 1188: my $httpurl=&hreflocation('',$url); 1189: $httpref{'httpref.'.$httpurl}=$srcurl; 1190: &Apache::lonnet::appenv(%httpref); 1191: } 1192: 1193: # --------- File operations in /home/httpd/html/userfiles/$domain/1/2/3/$course 1194: # input: action, courseID, current domain, intended 1195: # path to file, source of file, instruction to parse file for objects, 1196: # ref to hash for embedded objects, 1197: # ref to hash for codebase of java objects. 1198: # 1199: # output: url to file (if action was uploaddoc), 1200: # ok if successful, or diagnostic message otherwise (if action was propagate or copy) 1201: # 1202: # Allows directory structure to be used within lonUsers/../userfiles/ for a 1203: # course. 1204: # 1205: # action = propagate - /home/httpd/html/userfiles/$domain/1/2/3/$course/$file 1206: # will be copied to /home/httpd/lonUsers/1/2/3/$course/userfiles in 1207: # course's home server. 1208: # 1209: # action = copy - /home/httpd/html/userfiles/$domain/1/2/3/$course/$file will 1210: # be copied from $source (current location) to 1211: # /home/httpd/html/userfiles/$domain/1/2/3/$course/$file 1212: # and will then be copied to 1213: # /home/httpd/lonUsers/$domain/1/2/3/$course/userfiles/$file in 1214: # course's home server. 1215: # 1216: # action = uploaddoc - /home/httpd/html/userfiles/$domain/1/2/3/$course/$file 1217: # will be retrived from $env{form.uploaddoc} (from DOCS interface) to 1218: # /home/httpd/html/userfiles/$domain/1/2/3/$course/$file 1219: # and will then be copied to /home/httpd/lonUsers/1/2/3/$course/userfiles/$file 1220: # in course's home server. 1221: # 1222: 1223: sub process_coursefile { 1224: my ($action,$docuname,$docudom,$file,$source,$parser,$allfiles,$codebase)=@_; 1225: my $fetchresult; 1226: my $home=&homeserver($docuname,$docudom); 1227: if ($action eq 'propagate') { 1228: $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file, 1229: $home); 1230: } else { 1231: my $fpath = ''; 1232: my $fname = $file; 1233: ($fpath,$fname) = ($file =~ m|^(.*)/([^/]+)$|); 1234: $fpath=$docudom.'/'.$docuname.'/'.$fpath; 1235: my $filepath = &build_filepath($fpath); 1236: if ($action eq 'copy') { 1237: if ($source eq '') { 1238: $fetchresult = 'no source file'; 1239: return $fetchresult; 1240: } else { 1241: my $destination = $filepath.'/'.$fname; 1242: rename($source,$destination); 1243: $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file, 1244: $home); 1245: } 1246: } elsif ($action eq 'uploaddoc') { 1247: open(my $fh,'>'.$filepath.'/'.$fname); 1248: print $fh $env{'form.'.$source}; 1249: close($fh); 1250: if ($parser eq 'parse') { 1251: my $parse_result = &extract_embedded_items($filepath,$fname,$allfiles,$codebase); 1252: unless ($parse_result eq 'ok') { 1253: &logthis('Failed to parse '.$filepath.'/'.$fname.' for embedded media: '.$parse_result); 1254: } 1255: } 1256: $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file, 1257: $home); 1258: if ($fetchresult eq 'ok') { 1259: return '/uploaded/'.$fpath.'/'.$fname; 1260: } else { 1261: &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file. 1262: ' to host '.$home.': '.$fetchresult); 1263: return '/adm/notfound.html'; 1264: } 1265: } 1266: } 1267: unless ( $fetchresult eq 'ok') { 1268: &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file. 1269: ' to host '.$home.': '.$fetchresult); 1270: } 1271: return $fetchresult; 1272: } 1273: 1274: sub build_filepath { 1275: my ($fpath) = @_; 1276: my $filepath=$perlvar{'lonDocRoot'}.'/userfiles'; 1277: unless ($fpath eq '') { 1278: my @parts=split('/',$fpath); 1279: foreach my $part (@parts) { 1280: $filepath.= '/'.$part; 1281: if ((-e $filepath)!=1) { 1282: mkdir($filepath,0777); 1283: } 1284: } 1285: } 1286: return $filepath; 1287: } 1288: 1289: sub store_edited_file { 1290: my ($primary_url,$content,$docudom,$docuname,$fetchresult) = @_; 1291: my $file = $primary_url; 1292: $file =~ s#^/uploaded/$docudom/$docuname/##; 1293: my $fpath = ''; 1294: my $fname = $file; 1295: ($fpath,$fname) = ($file =~ m|^(.*)/([^/]+)$|); 1296: $fpath=$docudom.'/'.$docuname.'/'.$fpath; 1297: my $filepath = &build_filepath($fpath); 1298: open(my $fh,'>'.$filepath.'/'.$fname); 1299: print $fh $content; 1300: close($fh); 1301: my $home=&homeserver($docuname,$docudom); 1302: $$fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file, 1303: $home); 1304: if ($$fetchresult eq 'ok') { 1305: return '/uploaded/'.$fpath.'/'.$fname; 1306: } else { 1307: &logthis('Failed to transfer '.$docudom.'/'.$docuname.'/'.$file. 1308: ' to host '.$home.': '.$$fetchresult); 1309: return '/adm/notfound.html'; 1310: } 1311: } 1312: 1313: sub clean_filename { 1314: my ($fname)=@_; 1315: # Replace Windows backslashes by forward slashes 1316: $fname=~s/\\/\//g; 1317: # Get rid of everything but the actual filename 1318: $fname=~s/^.*\/([^\/]+)$/$1/; 1319: # Replace spaces by underscores 1320: $fname=~s/\s+/\_/g; 1321: # Replace all other weird characters by nothing 1322: $fname=~s/[^\w\.\-]//g; 1323: # Replace all .\d. sequences with _\d. so they no longer look like version 1324: # numbers 1325: $fname=~s/\.(\d+)(?=\.)/_$1/g; 1326: return $fname; 1327: } 1328: 1329: # --------------- Take an uploaded file and put it into the userfiles directory 1330: # input: $formname - the contents of the file are in $env{"form.$formname"} 1331: # the desired filenam is in $env{"form.$formname.filename"} 1332: # $coursedoc - if true up to the current course 1333: # if false 1334: # $subdir - directory in userfile to store the file into 1335: # $parser, $allfiles, $codebase - unknown 1336: # 1337: # output: url of file in userspace, or error: <message> 1338: # or /adm/notfound.html if failure to upload occurse 1339: 1340: 1341: sub userfileupload { 1342: my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase,$destuname,$destudom)=@_; 1343: if (!defined($subdir)) { $subdir='unknown'; } 1344: my $fname=$env{'form.'.$formname.'.filename'}; 1345: $fname=&clean_filename($fname); 1346: # See if there is anything left 1347: unless ($fname) { return 'error: no uploaded file'; } 1348: chop($env{'form.'.$formname}); 1349: if (($formname eq 'screenshot') && ($subdir eq 'helprequests')) { #files uploaded to help request form are handled differently 1350: my $now = time; 1351: my $filepath = 'tmp/helprequests/'.$now; 1352: my @parts=split(/\//,$filepath); 1353: my $fullpath = $perlvar{'lonDaemons'}; 1354: for (my $i=0;$i<@parts;$i++) { 1355: $fullpath .= '/'.$parts[$i]; 1356: if ((-e $fullpath)!=1) { 1357: mkdir($fullpath,0777); 1358: } 1359: } 1360: open(my $fh,'>'.$fullpath.'/'.$fname); 1361: print $fh $env{'form.'.$formname}; 1362: close($fh); 1363: return $fullpath.'/'.$fname; 1364: } 1365: 1366: # Create the directory if not present 1367: $fname="$subdir/$fname"; 1368: if ($coursedoc) { 1369: my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'}; 1370: my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'}; 1371: if ($env{'form.folder'} =~ m/^(default|supplemental)/) { 1372: return &finishuserfileupload($docuname,$docudom, 1373: $formname,$fname,$parser,$allfiles, 1374: $codebase); 1375: } else { 1376: $fname=$env{'form.folder'}.'/'.$fname; 1377: return &process_coursefile('uploaddoc',$docuname,$docudom, 1378: $fname,$formname,$parser, 1379: $allfiles,$codebase); 1380: } 1381: } elsif (defined($destuname)) { 1382: my $docuname=$destuname; 1383: my $docudom=$destudom; 1384: return &finishuserfileupload($docuname,$docudom,$formname, 1385: $fname,$parser,$allfiles,$codebase); 1386: 1387: } else { 1388: my $docuname=$env{'user.name'}; 1389: my $docudom=$env{'user.domain'}; 1390: if (exists($env{'form.group'})) { 1391: $docuname=$env{'course.'.$env{'request.course.id'}.'.num'}; 1392: $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'}; 1393: } 1394: return &finishuserfileupload($docuname,$docudom,$formname, 1395: $fname,$parser,$allfiles,$codebase); 1396: } 1397: } 1398: 1399: sub finishuserfileupload { 1400: my ($docuname,$docudom,$formname,$fname,$parser,$allfiles,$codebase) = @_; 1401: my $path=$docudom.'/'.$docuname.'/'; 1402: my $filepath=$perlvar{'lonDocRoot'}; 1403: my ($fnamepath,$file); 1404: $file=$fname; 1405: if ($fname=~m|/|) { 1406: ($fnamepath,$file) = ($fname =~ m|^(.*)/([^/]+)$|); 1407: $path.=$fnamepath.'/'; 1408: } 1409: my @parts=split(/\//,$filepath.'/userfiles/'.$path); 1410: my $count; 1411: for ($count=4;$count<=$#parts;$count++) { 1412: $filepath.="/$parts[$count]"; 1413: if ((-e $filepath)!=1) { 1414: mkdir($filepath,0777); 1415: } 1416: } 1417: # Save the file 1418: { 1419: if (!open(FH,'>'.$filepath.'/'.$file)) { 1420: &logthis('Failed to create '.$filepath.'/'.$file); 1421: print STDERR ('Failed to create '.$filepath.'/'.$file."\n"); 1422: return '/adm/notfound.html'; 1423: } 1424: if (!print FH ($env{'form.'.$formname})) { 1425: &logthis('Failed to write to '.$filepath.'/'.$file); 1426: print STDERR ('Failed to write to '.$filepath.'/'.$file."\n"); 1427: return '/adm/notfound.html'; 1428: } 1429: close(FH); 1430: } 1431: if ($parser eq 'parse') { 1432: my $parse_result = &extract_embedded_items($filepath,$file,$allfiles, 1433: $codebase); 1434: unless ($parse_result eq 'ok') { 1435: &logthis('Failed to parse '.$filepath.$file. 1436: ' for embedded media: '.$parse_result); 1437: } 1438: } 1439: # Notify homeserver to grep it 1440: # 1441: my $docuhome=&homeserver($docuname,$docudom); 1442: my $fetchresult= &reply('fetchuserfile:'.$path.$file,$docuhome); 1443: if ($fetchresult eq 'ok') { 1444: # 1445: # Return the URL to it 1446: return '/uploaded/'.$path.$file; 1447: } else { 1448: &logthis('Failed to transfer '.$path.$file.' to host '.$docuhome. 1449: ': '.$fetchresult); 1450: return '/adm/notfound.html'; 1451: } 1452: } 1453: 1454: sub extract_embedded_items { 1455: my ($filepath,$file,$allfiles,$codebase,$content) = @_; 1456: my @state = (); 1457: my %javafiles = ( 1458: codebase => '', 1459: code => '', 1460: archive => '' 1461: ); 1462: my %mediafiles = ( 1463: src => '', 1464: movie => '', 1465: ); 1466: my $p; 1467: if ($content) { 1468: $p = HTML::LCParser->new($content); 1469: } else { 1470: $p = HTML::LCParser->new($filepath.'/'.$file); 1471: } 1472: while (my $t=$p->get_token()) { 1473: if ($t->[0] eq 'S') { 1474: my ($tagname, $attr) = ($t->[1],$t->[2]); 1475: push (@state, $tagname); 1476: if (lc($tagname) eq 'allow') { 1477: &add_filetype($allfiles,$attr->{'src'},'src'); 1478: } 1479: if (lc($tagname) eq 'img') { 1480: &add_filetype($allfiles,$attr->{'src'},'src'); 1481: } 1482: if (lc($tagname) eq 'script') { 1483: if ($attr->{'archive'} =~ /\.jar$/i) { 1484: &add_filetype($allfiles,$attr->{'archive'},'archive'); 1485: } else { 1486: &add_filetype($allfiles,$attr->{'src'},'src'); 1487: } 1488: } 1489: if (lc($tagname) eq 'link') { 1490: if (lc($attr->{'rel'}) eq 'stylesheet') { 1491: &add_filetype($allfiles,$attr->{'href'},'href'); 1492: } 1493: } 1494: if (lc($tagname) eq 'object' || 1495: (lc($tagname) eq 'embed' && lc($state[-2]) ne 'object')) { 1496: foreach my $item (keys(%javafiles)) { 1497: $javafiles{$item} = ''; 1498: } 1499: } 1500: if (lc($state[-2]) eq 'object' && lc($tagname) eq 'param') { 1501: my $name = lc($attr->{'name'}); 1502: foreach my $item (keys(%javafiles)) { 1503: if ($name eq $item) { 1504: $javafiles{$item} = $attr->{'value'}; 1505: last; 1506: } 1507: } 1508: foreach my $item (keys(%mediafiles)) { 1509: if ($name eq $item) { 1510: &add_filetype($allfiles, $attr->{'value'}, 'value'); 1511: last; 1512: } 1513: } 1514: } 1515: if (lc($tagname) eq 'embed' || lc($tagname) eq 'applet') { 1516: foreach my $item (keys(%javafiles)) { 1517: if ($attr->{$item}) { 1518: $javafiles{$item} = $attr->{$item}; 1519: last; 1520: } 1521: } 1522: foreach my $item (keys(%mediafiles)) { 1523: if ($attr->{$item}) { 1524: &add_filetype($allfiles,$attr->{$item},$item); 1525: last; 1526: } 1527: } 1528: } 1529: } elsif ($t->[0] eq 'E') { 1530: my ($tagname) = ($t->[1]); 1531: if ($javafiles{'codebase'} ne '') { 1532: $javafiles{'codebase'} .= '/'; 1533: } 1534: if (lc($tagname) eq 'applet' || 1535: lc($tagname) eq 'object' || 1536: (lc($tagname) eq 'embed' && lc($state[-2]) ne 'object') 1537: ) { 1538: foreach my $item (keys(%javafiles)) { 1539: if ($item ne 'codebase' && $javafiles{$item} ne '') { 1540: my $file=$javafiles{'codebase'}.$javafiles{$item}; 1541: &add_filetype($allfiles,$file,$item); 1542: } 1543: } 1544: } 1545: pop @state; 1546: } 1547: } 1548: return 'ok'; 1549: } 1550: 1551: sub add_filetype { 1552: my ($allfiles,$file,$type)=@_; 1553: if (exists($allfiles->{$file})) { 1554: unless (grep/^\Q$type\E$/, @{$allfiles->{$file}}) { 1555: push(@{$allfiles->{$file}}, &escape($type)); 1556: } 1557: } else { 1558: @{$allfiles->{$file}} = (&escape($type)); 1559: } 1560: } 1561: 1562: sub removeuploadedurl { 1563: my ($url)=@_; 1564: my (undef,undef,$udom,$uname,$fname)=split('/',$url,5); 1565: return &removeuserfile($uname,$udom,$fname); 1566: } 1567: 1568: sub removeuserfile { 1569: my ($docuname,$docudom,$fname)=@_; 1570: my $home=&homeserver($docuname,$docudom); 1571: return &reply("removeuserfile:$docudom/$docuname/$fname",$home); 1572: } 1573: 1574: sub mkdiruserfile { 1575: my ($docuname,$docudom,$dir)=@_; 1576: my $home=&homeserver($docuname,$docudom); 1577: return &reply("mkdiruserfile:".&escape("$docudom/$docuname/$dir"),$home); 1578: } 1579: 1580: sub renameuserfile { 1581: my ($docuname,$docudom,$old,$new)=@_; 1582: my $home=&homeserver($docuname,$docudom); 1583: return &reply("renameuserfile:$docudom:$docuname:".&escape("$old").':'. 1584: &escape("$new"),$home); 1585: } 1586: 1587: # ------------------------------------------------------------------------- Log 1588: 1589: sub log { 1590: my ($dom,$nam,$hom,$what)=@_; 1591: return critical("log:$dom:$nam:$what",$hom); 1592: } 1593: 1594: # ------------------------------------------------------------------ Course Log 1595: # 1596: # This routine flushes several buffers of non-mission-critical nature 1597: # 1598: 1599: sub flushcourselogs { 1600: &logthis('Flushing log buffers'); 1601: # 1602: # course logs 1603: # This is a log of all transactions in a course, which can be used 1604: # for data mining purposes 1605: # 1606: # It also collects the courseid database, which lists last transaction 1607: # times and course titles for all courseids 1608: # 1609: my %courseidbuffer=(); 1610: foreach (keys %courselogs) { 1611: my $crsid=$_; 1612: if (&reply('log:'.$coursedombuf{$crsid}.':'.$coursenumbuf{$crsid}.':'. 1613: &escape($courselogs{$crsid}), 1614: $coursehombuf{$crsid}) eq 'ok') { 1615: delete $courselogs{$crsid}; 1616: } else { 1617: &logthis('Failed to flush log buffer for '.$crsid); 1618: if (length($courselogs{$crsid})>40000) { 1619: &logthis("<font color=\"blue\">WARNING: Buffer for ".$crsid. 1620: " exceeded maximum size, deleting.</font>"); 1621: delete $courselogs{$crsid}; 1622: } 1623: } 1624: if ($courseidbuffer{$coursehombuf{$crsid}}) { 1625: $courseidbuffer{$coursehombuf{$crsid}}.='&'. 1626: &escape($crsid).'='.&escape($coursedescrbuf{$crsid}). 1627: ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid}); 1628: } else { 1629: $courseidbuffer{$coursehombuf{$crsid}}= 1630: &escape($crsid).'='.&escape($coursedescrbuf{$crsid}). 1631: ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid}); 1632: } 1633: } 1634: # 1635: # Write course id database (reverse lookup) to homeserver of courses 1636: # Is used in pickcourse 1637: # 1638: foreach (keys %courseidbuffer) { 1639: &courseidput($hostdom{$_},$courseidbuffer{$_},$_); 1640: } 1641: # 1642: # File accesses 1643: # Writes to the dynamic metadata of resources to get hit counts, etc. 1644: # 1645: foreach my $entry (keys(%accesshash)) { 1646: if ($entry =~ /___count$/) { 1647: my ($dom,$name); 1648: ($dom,$name,undef)=($entry=~m:___(\w+)/(\w+)/(.*)___count$:); 1649: if (! defined($dom) || $dom eq '' || 1650: ! defined($name) || $name eq '') { 1651: my $cid = $env{'request.course.id'}; 1652: $dom = $env{'request.'.$cid.'.domain'}; 1653: $name = $env{'request.'.$cid.'.num'}; 1654: } 1655: my $value = $accesshash{$entry}; 1656: my (undef,$url,undef) = ($entry =~ /^(.*)___(.*)___count$/); 1657: my %temphash=($url => $value); 1658: my $result = &inc('nohist_accesscount',\%temphash,$dom,$name); 1659: if ($result eq 'ok') { 1660: delete $accesshash{$entry}; 1661: } elsif ($result eq 'unknown_cmd') { 1662: # Target server has old code running on it. 1663: my %temphash=($entry => $value); 1664: if (&put('nohist_resevaldata',\%temphash,$dom,$name) eq 'ok') { 1665: delete $accesshash{$entry}; 1666: } 1667: } 1668: } else { 1669: my ($dom,$name) = ($entry=~m:___(\w+)/(\w+)/(.*)___(\w+)$:); 1670: my %temphash=($entry => $accesshash{$entry}); 1671: if (&put('nohist_resevaldata',\%temphash,$dom,$name) eq 'ok') { 1672: delete $accesshash{$entry}; 1673: } 1674: } 1675: } 1676: # 1677: # Roles 1678: # Reverse lookup of user roles for course faculty/staff and co-authorship 1679: # 1680: foreach (keys %userrolehash) { 1681: my $entry=$_; 1682: my ($role,$uname,$udom,$runame,$rudom,$rsec)= 1683: split(/\:/,$entry); 1684: if (&Apache::lonnet::put('nohist_userroles', 1685: { $role.':'.$uname.':'.$udom.':'.$rsec => $userrolehash{$entry} }, 1686: $rudom,$runame) eq 'ok') { 1687: delete $userrolehash{$entry}; 1688: } 1689: } 1690: # 1691: # Reverse lookup of domain roles (dc, ad, li, sc, au) 1692: # 1693: my %domrolebuffer = (); 1694: foreach my $entry (keys %domainrolehash) { 1695: my ($role,$uname,$udom,$runame,$rudom,$rsec)=split/:/,$entry; 1696: if ($domrolebuffer{$rudom}) { 1697: $domrolebuffer{$rudom}.='&'.&escape($entry). 1698: '='.&escape($domainrolehash{$entry}); 1699: } else { 1700: $domrolebuffer{$rudom}.=&escape($entry). 1701: '='.&escape($domainrolehash{$entry}); 1702: } 1703: delete $domainrolehash{$entry}; 1704: } 1705: foreach my $dom (keys(%domrolebuffer)) { 1706: foreach my $tryserver (keys %libserv) { 1707: if ($hostdom{$tryserver} eq $dom) { 1708: unless (&reply('domroleput:'.$dom.':'. 1709: $domrolebuffer{$dom},$tryserver) eq 'ok') { 1710: &logthis('Put of domain roles failed for '.$dom.' and '.$tryserver); 1711: } 1712: } 1713: } 1714: } 1715: $dumpcount++; 1716: } 1717: 1718: sub courselog { 1719: my $what=shift; 1720: $what=time.':'.$what; 1721: unless ($env{'request.course.id'}) { return ''; } 1722: $coursedombuf{$env{'request.course.id'}}= 1723: $env{'course.'.$env{'request.course.id'}.'.domain'}; 1724: $coursenumbuf{$env{'request.course.id'}}= 1725: $env{'course.'.$env{'request.course.id'}.'.num'}; 1726: $coursehombuf{$env{'request.course.id'}}= 1727: $env{'course.'.$env{'request.course.id'}.'.home'}; 1728: $coursedescrbuf{$env{'request.course.id'}}= 1729: $env{'course.'.$env{'request.course.id'}.'.description'}; 1730: $courseinstcodebuf{$env{'request.course.id'}}= 1731: $env{'course.'.$env{'request.course.id'}.'.internal.coursecode'}; 1732: $courseownerbuf{$env{'request.course.id'}}= 1733: $env{'course.'.$env{'request.course.id'}.'.internal.courseowner'}; 1734: if (defined $courselogs{$env{'request.course.id'}}) { 1735: $courselogs{$env{'request.course.id'}}.='&'.$what; 1736: } else { 1737: $courselogs{$env{'request.course.id'}}.=$what; 1738: } 1739: if (length($courselogs{$env{'request.course.id'}})>4048) { 1740: &flushcourselogs(); 1741: } 1742: } 1743: 1744: sub courseacclog { 1745: my $fnsymb=shift; 1746: unless ($env{'request.course.id'}) { return ''; } 1747: my $what=$fnsymb.':'.$env{'user.name'}.':'.$env{'user.domain'}; 1748: if ($fnsymb=~/(problem|exam|quiz|assess|survey|form|task|page)$/) { 1749: $what.=':POST'; 1750: # FIXME: Probably ought to escape things.... 1751: foreach (keys %env) { 1752: if ($_=~/^form\.(.*)/) { 1753: $what.=':'.$1.'='.$env{$_}; 1754: } 1755: } 1756: } elsif ($fnsymb =~ m:^/adm/searchcat:) { 1757: # FIXME: We should not be depending on a form parameter that someone 1758: # editing lonsearchcat.pm might change in the future. 1759: if ($env{'form.phase'} eq 'course_search') { 1760: $what.= ':POST'; 1761: # FIXME: Probably ought to escape things.... 1762: foreach my $element ('courseexp','crsfulltext','crsrelated', 1763: 'crsdiscuss') { 1764: $what.=':'.$element.'='.$env{'form.'.$element}; 1765: } 1766: } 1767: } 1768: &courselog($what); 1769: } 1770: 1771: sub countacc { 1772: my $url=&declutter(shift); 1773: return if (! defined($url) || $url eq ''); 1774: unless ($env{'request.course.id'}) { return ''; } 1775: $accesshash{$env{'request.course.id'}.'___'.$url.'___course'}=1; 1776: my $key=$$.$processmarker.'_'.$dumpcount.'___'.$url.'___count'; 1777: $accesshash{$key}++; 1778: } 1779: 1780: sub linklog { 1781: my ($from,$to)=@_; 1782: $from=&declutter($from); 1783: $to=&declutter($to); 1784: $accesshash{$from.'___'.$to.'___comefrom'}=1; 1785: $accesshash{$to.'___'.$from.'___goto'}=1; 1786: } 1787: 1788: sub userrolelog { 1789: my ($trole,$username,$domain,$area,$tstart,$tend)=@_; 1790: if (($trole=~/^ca/) || ($trole=~/^aa/) || 1791: ($trole=~/^in/) || ($trole=~/^cc/) || 1792: ($trole=~/^ep/) || ($trole=~/^cr/) || 1793: ($trole=~/^ta/)) { 1794: my (undef,$rudom,$runame,$rsec)=split(/\//,$area); 1795: $userrolehash 1796: {$trole.':'.$username.':'.$domain.':'.$runame.':'.$rudom.':'.$rsec} 1797: =$tend.':'.$tstart; 1798: } 1799: if (($trole=~/^dc/) || ($trole=~/^ad/) || 1800: ($trole=~/^li/) || ($trole=~/^li/) || 1801: ($trole=~/^au/) || ($trole=~/^dg/) || 1802: ($trole=~/^sc/)) { 1803: my (undef,$rudom,$runame,$rsec)=split(/\//,$area); 1804: $domainrolehash 1805: {$trole.':'.$username.':'.$domain.':'.$runame.':'.$rudom.':'.$rsec} 1806: = $tend.':'.$tstart; 1807: } 1808: } 1809: 1810: sub get_course_adv_roles { 1811: my $cid=shift; 1812: $cid=$env{'request.course.id'} unless (defined($cid)); 1813: my %coursehash=&coursedescription($cid); 1814: my %nothide=(); 1815: foreach (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) { 1816: $nothide{join(':',split(/[\@\:]/,$_))}=1; 1817: } 1818: my %returnhash=(); 1819: my %dumphash= 1820: &dump('nohist_userroles',$coursehash{'domain'},$coursehash{'num'}); 1821: my $now=time; 1822: foreach (keys %dumphash) { 1823: my ($tend,$tstart)=split(/\:/,$dumphash{$_}); 1824: if (($tstart) && ($tstart<0)) { next; } 1825: if (($tend) && ($tend<$now)) { next; } 1826: if (($tstart) && ($now<$tstart)) { next; } 1827: my ($role,$username,$domain,$section)=split(/\:/,$_); 1828: if ($username eq '' || $domain eq '') { next; } 1829: if ((&privileged($username,$domain)) && 1830: (!$nothide{$username.':'.$domain})) { next; } 1831: if ($role eq 'cr') { next; } 1832: my $key=&plaintext($role); 1833: if ($role =~ /^cr/) { 1834: $key=(split('/',$role))[3]; 1835: } 1836: if ($section) { $key.=' (Sec/Grp '.$section.')'; } 1837: if ($returnhash{$key}) { 1838: $returnhash{$key}.=','.$username.':'.$domain; 1839: } else { 1840: $returnhash{$key}=$username.':'.$domain; 1841: } 1842: } 1843: return %returnhash; 1844: } 1845: 1846: sub get_my_roles { 1847: my ($uname,$udom)=@_; 1848: unless (defined($uname)) { $uname=$env{'user.name'}; } 1849: unless (defined($udom)) { $udom=$env{'user.domain'}; } 1850: my %dumphash= 1851: &dump('nohist_userroles',$udom,$uname); 1852: my %returnhash=(); 1853: my $now=time; 1854: foreach (keys %dumphash) { 1855: my ($tend,$tstart)=split(/\:/,$dumphash{$_}); 1856: if (($tstart) && ($tstart<0)) { next; } 1857: if (($tend) && ($tend<$now)) { next; } 1858: if (($tstart) && ($now<$tstart)) { next; } 1859: my ($role,$username,$domain,$section)=split(/\:/,$_); 1860: $returnhash{$username.':'.$domain.':'.$role}=$tstart.':'.$tend; 1861: } 1862: return %returnhash; 1863: } 1864: 1865: # ----------------------------------------------------- Frontpage Announcements 1866: # 1867: # 1868: 1869: sub postannounce { 1870: my ($server,$text)=@_; 1871: unless (&allowed('psa',$hostdom{$server})) { return 'refused'; } 1872: unless ($text=~/\w/) { $text=''; } 1873: return &reply('setannounce:'.&escape($text),$server); 1874: } 1875: 1876: sub getannounce { 1877: 1878: if (open(my $fh,$perlvar{'lonDocRoot'}.'/announcement.txt')) { 1879: my $announcement=''; 1880: while (<$fh>) { $announcement .=$_; } 1881: close($fh); 1882: if ($announcement=~/\w/) { 1883: return 1884: '<table bgcolor="#FF5555" cellpadding="5" cellspacing="3">'. 1885: '<tr><td bgcolor="#FFFFFF"><tt>'.$announcement.'</tt></td></tr></table>'; 1886: } else { 1887: return ''; 1888: } 1889: } else { 1890: return ''; 1891: } 1892: } 1893: 1894: # ---------------------------------------------------------- Course ID routines 1895: # Deal with domain's nohist_courseid.db files 1896: # 1897: 1898: sub courseidput { 1899: my ($domain,$what,$coursehome)=@_; 1900: return &reply('courseidput:'.$domain.':'.$what,$coursehome); 1901: } 1902: 1903: sub courseiddump { 1904: my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,$coursefilter,$hostidflag,$hostidref)=@_; 1905: my %returnhash=(); 1906: unless ($domfilter) { $domfilter=''; } 1907: foreach my $tryserver (keys %libserv) { 1908: if ( ($hostidflag == 1 && grep/^$tryserver$/,@{$hostidref}) || (!defined($hostidflag)) ) { 1909: if ((!$domfilter) || ($hostdom{$tryserver} eq $domfilter)) { 1910: foreach ( 1911: split(/\&/,&reply('courseiddump:'.$hostdom{$tryserver}.':'. 1912: $sincefilter.':'.&escape($descfilter).':'. 1913: &escape($instcodefilter).':'.&escape($ownerfilter).':'.&escape($coursefilter), 1914: $tryserver))) { 1915: my ($key,$value)=split(/\=/,$_); 1916: if (($key) && ($value)) { 1917: $returnhash{&unescape($key)}=$value; 1918: } 1919: } 1920: } 1921: } 1922: } 1923: return %returnhash; 1924: } 1925: 1926: # ---------------------------------------------------------- DC e-mail 1927: 1928: sub dcmailput { 1929: my ($domain,$msgid,$message,$server)=@_; 1930: my $status = &Apache::lonnet::critical( 1931: 'dcmailput:'.$domain.':'.&Apache::lonnet::escape($msgid).'='. 1932: &Apache::lonnet::escape($message),$server); 1933: return $status; 1934: } 1935: 1936: sub dcmaildump { 1937: my ($dom,$startdate,$enddate,$senders) = @_; 1938: my %returnhash=(); 1939: if (exists($domain_primary{$dom})) { 1940: my $cmd='dcmaildump:'.$dom.':'.&escape($startdate).':'. 1941: &escape($enddate).':'; 1942: my @esc_senders=map { &escape($_)} @$senders; 1943: $cmd.=&escape(join('&',@esc_senders)); 1944: foreach (split(/\&/,&reply($cmd,$domain_primary{$dom}))) { 1945: my ($key,$value) = split(/\=/,$_); 1946: if (($key) && ($value)) { 1947: $returnhash{&unescape($key)} = &unescape($value); 1948: } 1949: } 1950: } 1951: return %returnhash; 1952: } 1953: # ---------------------------------------------------------- Domain roles 1954: 1955: sub get_domain_roles { 1956: my ($dom,$roles,$startdate,$enddate)=@_; 1957: if (undef($startdate) || $startdate eq '') { 1958: $startdate = '.'; 1959: } 1960: if (undef($enddate) || $enddate eq '') { 1961: $enddate = '.'; 1962: } 1963: my $rolelist = join(':',@{$roles}); 1964: my %personnel = (); 1965: foreach my $tryserver (keys(%libserv)) { 1966: if ($hostdom{$tryserver} eq $dom) { 1967: %{$personnel{$tryserver}}=(); 1968: foreach ( 1969: split(/\&/,&reply('domrolesdump:'.$dom.':'. 1970: &escape($startdate).':'.&escape($enddate).':'. 1971: &escape($rolelist), $tryserver))) { 1972: my($key,$value) = split(/\=/,$_); 1973: if (($key) && ($value)) { 1974: $personnel{$tryserver}{&unescape($key)} = &unescape($value); 1975: } 1976: } 1977: } 1978: } 1979: return %personnel; 1980: } 1981: 1982: # ----------------------------------------------------------- Check out an item 1983: 1984: sub get_first_access { 1985: my ($type,$argsymb)=@_; 1986: my ($symb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser(); 1987: if ($argsymb) { $symb=$argsymb; } 1988: my ($map,$id,$res)=&decode_symb($symb); 1989: if ($type eq 'map') { 1990: $res=&symbread($map); 1991: } else { 1992: $res=$symb; 1993: } 1994: my %times=&get('firstaccesstimes',["$courseid\0$res"],$udom,$uname); 1995: return $times{"$courseid\0$res"}; 1996: } 1997: 1998: sub set_first_access { 1999: my ($type)=@_; 2000: my ($symb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser(); 2001: my ($map,$id,$res)=&decode_symb($symb); 2002: if ($type eq 'map') { 2003: $res=&symbread($map); 2004: } else { 2005: $res=$symb; 2006: } 2007: my $firstaccess=&get_first_access($type,$symb); 2008: if (!$firstaccess) { 2009: return &put('firstaccesstimes',{"$courseid\0$res"=>time},$udom,$uname); 2010: } 2011: return 'already_set'; 2012: } 2013: 2014: sub checkout { 2015: my ($symb,$tuname,$tudom,$tcrsid)=@_; 2016: my $now=time; 2017: my $lonhost=$perlvar{'lonHostID'}; 2018: my $infostr=&escape( 2019: 'CHECKOUTTOKEN&'. 2020: $tuname.'&'. 2021: $tudom.'&'. 2022: $tcrsid.'&'. 2023: $symb.'&'. 2024: $now.'&'.$ENV{'REMOTE_ADDR'}); 2025: my $token=&reply('tmpput:'.$infostr,$lonhost); 2026: if ($token=~/^error\:/) { 2027: &logthis("<font color=\"blue\">WARNING: ". 2028: "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb. 2029: "</font>"); 2030: return ''; 2031: } 2032: 2033: $token=~s/^(\d+)\_.*\_(\d+)$/$1\*$2\*$lonhost/; 2034: $token=~tr/a-z/A-Z/; 2035: 2036: my %infohash=('resource.0.outtoken' => $token, 2037: 'resource.0.checkouttime' => $now, 2038: 'resource.0.outremote' => $ENV{'REMOTE_ADDR'}); 2039: 2040: unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') { 2041: return ''; 2042: } else { 2043: &logthis("<font color=\"blue\">WARNING: ". 2044: "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb. 2045: "</font>"); 2046: } 2047: 2048: if (&log($tudom,$tuname,&homeserver($tuname,$tudom), 2049: &escape('Checkout '.$infostr.' - '. 2050: $token)) ne 'ok') { 2051: return ''; 2052: } else { 2053: &logthis("<font color=\"blue\">WARNING: ". 2054: "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb. 2055: "</font>"); 2056: } 2057: return $token; 2058: } 2059: 2060: # ------------------------------------------------------------ Check in an item 2061: 2062: sub checkin { 2063: my $token=shift; 2064: my $now=time; 2065: my ($ta,$tb,$lonhost)=split(/\*/,$token); 2066: $lonhost=~tr/A-Z/a-z/; 2067: my $dtoken=$ta.'_'.$hostname{$lonhost}.'_'.$tb; 2068: $dtoken=~s/\W/\_/g; 2069: my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)= 2070: split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost))); 2071: 2072: unless (($tuname) && ($tudom)) { 2073: &logthis('Check in '.$token.' ('.$dtoken.') failed'); 2074: return ''; 2075: } 2076: 2077: unless (&allowed('mgr',$tcrsid)) { 2078: &logthis('Check in '.$token.' ('.$dtoken.') unauthorized: '. 2079: $env{'user.name'}.' - '.$env{'user.domain'}); 2080: return ''; 2081: } 2082: 2083: my %infohash=('resource.0.intoken' => $token, 2084: 'resource.0.checkintime' => $now, 2085: 'resource.0.inremote' => $ENV{'REMOTE_ADDR'}); 2086: 2087: unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') { 2088: return ''; 2089: } 2090: 2091: if (&log($tudom,$tuname,&homeserver($tuname,$tudom), 2092: &escape('Checkin - '.$token)) ne 'ok') { 2093: return ''; 2094: } 2095: 2096: return ($symb,$tuname,$tudom,$tcrsid); 2097: } 2098: 2099: # --------------------------------------------- Set Expire Date for Spreadsheet 2100: 2101: sub expirespread { 2102: my ($uname,$udom,$stype,$usymb)=@_; 2103: my $cid=$env{'request.course.id'}; 2104: if ($cid) { 2105: my $now=time; 2106: my $key=$uname.':'.$udom.':'.$stype.':'.$usymb; 2107: return &reply('put:'.$env{'course.'.$cid.'.domain'}.':'. 2108: $env{'course.'.$cid.'.num'}. 2109: ':nohist_expirationdates:'. 2110: &escape($key).'='.$now, 2111: $env{'course.'.$cid.'.home'}) 2112: } 2113: return 'ok'; 2114: } 2115: 2116: # ----------------------------------------------------- Devalidate Spreadsheets 2117: 2118: sub devalidate { 2119: my ($symb,$uname,$udom)=@_; 2120: my $cid=$env{'request.course.id'}; 2121: if ($cid) { 2122: # delete the stored spreadsheets for 2123: # - the student level sheet of this user in course's homespace 2124: # - the assessment level sheet for this resource 2125: # for this user in user's homespace 2126: # - current conditional state info 2127: my $key=$uname.':'.$udom.':'; 2128: my $status= 2129: &del('nohist_calculatedsheets', 2130: [$key.'studentcalc:'], 2131: $env{'course.'.$cid.'.domain'}, 2132: $env{'course.'.$cid.'.num'}) 2133: .' '. 2134: &del('nohist_calculatedsheets_'.$cid, 2135: [$key.'assesscalc:'.$symb],$udom,$uname); 2136: unless ($status eq 'ok ok') { 2137: &logthis('Could not devalidate spreadsheet '. 2138: $uname.' at '.$udom.' for '. 2139: $symb.': '.$status); 2140: } 2141: &delenv('user.state.'.$cid); 2142: } 2143: } 2144: 2145: sub get_scalar { 2146: my ($string,$end) = @_; 2147: my $value; 2148: if ($$string =~ s/^([^&]*?)($end)/$2/) { 2149: $value = $1; 2150: } elsif ($$string =~ s/^([^&]*?)&//) { 2151: $value = $1; 2152: } 2153: return &unescape($value); 2154: } 2155: 2156: sub array2str { 2157: my (@array) = @_; 2158: my $result=&arrayref2str(\@array); 2159: $result=~s/^__ARRAY_REF__//; 2160: $result=~s/__END_ARRAY_REF__$//; 2161: return $result; 2162: } 2163: 2164: sub arrayref2str { 2165: my ($arrayref) = @_; 2166: my $result='__ARRAY_REF__'; 2167: foreach my $elem (@$arrayref) { 2168: if(ref($elem) eq 'ARRAY') { 2169: $result.=&arrayref2str($elem).'&'; 2170: } elsif(ref($elem) eq 'HASH') { 2171: $result.=&hashref2str($elem).'&'; 2172: } elsif(ref($elem)) { 2173: #print("Got a ref of ".(ref($elem))." skipping."); 2174: } else { 2175: $result.=&escape($elem).'&'; 2176: } 2177: } 2178: $result=~s/\&$//; 2179: $result .= '__END_ARRAY_REF__'; 2180: return $result; 2181: } 2182: 2183: sub hash2str { 2184: my (%hash) = @_; 2185: my $result=&hashref2str(\%hash); 2186: $result=~s/^__HASH_REF__//; 2187: $result=~s/__END_HASH_REF__$//; 2188: return $result; 2189: } 2190: 2191: sub hashref2str { 2192: my ($hashref)=@_; 2193: my $result='__HASH_REF__'; 2194: foreach (sort(keys(%$hashref))) { 2195: if (ref($_) eq 'ARRAY') { 2196: $result.=&arrayref2str($_).'='; 2197: } elsif (ref($_) eq 'HASH') { 2198: $result.=&hashref2str($_).'='; 2199: } elsif (ref($_)) { 2200: $result.='='; 2201: #print("Got a ref of ".(ref($_))." skipping."); 2202: } else { 2203: if ($_) {$result.=&escape($_).'=';} else { last; } 2204: } 2205: 2206: if(ref($hashref->{$_}) eq 'ARRAY') { 2207: $result.=&arrayref2str($hashref->{$_}).'&'; 2208: } elsif(ref($hashref->{$_}) eq 'HASH') { 2209: $result.=&hashref2str($hashref->{$_}).'&'; 2210: } elsif(ref($hashref->{$_})) { 2211: $result.='&'; 2212: #print("Got a ref of ".(ref($hashref->{$_}))." skipping."); 2213: } else { 2214: $result.=&escape($hashref->{$_}).'&'; 2215: } 2216: } 2217: $result=~s/\&$//; 2218: $result .= '__END_HASH_REF__'; 2219: return $result; 2220: } 2221: 2222: sub str2hash { 2223: my ($string)=@_; 2224: my ($hash)=&str2hashref('__HASH_REF__'.$string.'__END_HASH_REF__'); 2225: return %$hash; 2226: } 2227: 2228: sub str2hashref { 2229: my ($string) = @_; 2230: 2231: my %hash; 2232: 2233: if($string !~ /^__HASH_REF__/) { 2234: if (! ($string eq '' || !defined($string))) { 2235: $hash{'error'}='Not hash reference'; 2236: } 2237: return (\%hash, $string); 2238: } 2239: 2240: $string =~ s/^__HASH_REF__//; 2241: 2242: while($string !~ /^__END_HASH_REF__/) { 2243: #key 2244: my $key=''; 2245: if($string =~ /^__HASH_REF__/) { 2246: ($key, $string)=&str2hashref($string); 2247: if(defined($key->{'error'})) { 2248: $hash{'error'}='Bad data'; 2249: return (\%hash, $string); 2250: } 2251: } elsif($string =~ /^__ARRAY_REF__/) { 2252: ($key, $string)=&str2arrayref($string); 2253: if($key->[0] eq 'Array reference error') { 2254: $hash{'error'}='Bad data'; 2255: return (\%hash, $string); 2256: } 2257: } else { 2258: $string =~ s/^(.*?)=//; 2259: $key=&unescape($1); 2260: } 2261: $string =~ s/^=//; 2262: 2263: #value 2264: my $value=''; 2265: if($string =~ /^__HASH_REF__/) { 2266: ($value, $string)=&str2hashref($string); 2267: if(defined($value->{'error'})) { 2268: $hash{'error'}='Bad data'; 2269: return (\%hash, $string); 2270: } 2271: } elsif($string =~ /^__ARRAY_REF__/) { 2272: ($value, $string)=&str2arrayref($string); 2273: if($value->[0] eq 'Array reference error') { 2274: $hash{'error'}='Bad data'; 2275: return (\%hash, $string); 2276: } 2277: } else { 2278: $value=&get_scalar(\$string,'__END_HASH_REF__'); 2279: } 2280: $string =~ s/^&//; 2281: 2282: $hash{$key}=$value; 2283: } 2284: 2285: $string =~ s/^__END_HASH_REF__//; 2286: 2287: return (\%hash, $string); 2288: } 2289: 2290: sub str2array { 2291: my ($string)=@_; 2292: my ($array)=&str2arrayref('__ARRAY_REF__'.$string.'__END_ARRAY_REF__'); 2293: return @$array; 2294: } 2295: 2296: sub str2arrayref { 2297: my ($string) = @_; 2298: my @array; 2299: 2300: if($string !~ /^__ARRAY_REF__/) { 2301: if (! ($string eq '' || !defined($string))) { 2302: $array[0]='Array reference error'; 2303: } 2304: return (\@array, $string); 2305: } 2306: 2307: $string =~ s/^__ARRAY_REF__//; 2308: 2309: while($string !~ /^__END_ARRAY_REF__/) { 2310: my $value=''; 2311: if($string =~ /^__HASH_REF__/) { 2312: ($value, $string)=&str2hashref($string); 2313: if(defined($value->{'error'})) { 2314: $array[0] ='Array reference error'; 2315: return (\@array, $string); 2316: } 2317: } elsif($string =~ /^__ARRAY_REF__/) { 2318: ($value, $string)=&str2arrayref($string); 2319: if($value->[0] eq 'Array reference error') { 2320: $array[0] ='Array reference error'; 2321: return (\@array, $string); 2322: } 2323: } else { 2324: $value=&get_scalar(\$string,'__END_ARRAY_REF__'); 2325: } 2326: $string =~ s/^&//; 2327: 2328: push(@array, $value); 2329: } 2330: 2331: $string =~ s/^__END_ARRAY_REF__//; 2332: 2333: return (\@array, $string); 2334: } 2335: 2336: # -------------------------------------------------------------------Temp Store 2337: 2338: sub tmpreset { 2339: my ($symb,$namespace,$domain,$stuname) = @_; 2340: if (!$symb) { 2341: $symb=&symbread(); 2342: if (!$symb) { $symb= $env{'request.url'}; } 2343: } 2344: $symb=escape($symb); 2345: 2346: if (!$namespace) { $namespace=$env{'request.state'}; } 2347: $namespace=~s/\//\_/g; 2348: $namespace=~s/\W//g; 2349: 2350: if (!$domain) { $domain=$env{'user.domain'}; } 2351: if (!$stuname) { $stuname=$env{'user.name'}; } 2352: if ($domain eq 'public' && $stuname eq 'public') { 2353: $stuname=$ENV{'REMOTE_ADDR'}; 2354: } 2355: my $path=$perlvar{'lonDaemons'}.'/tmp'; 2356: my %hash; 2357: if (tie(%hash,'GDBM_File', 2358: $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db', 2359: &GDBM_WRCREAT(),0640)) { 2360: foreach my $key (keys %hash) { 2361: if ($key=~ /:$symb/) { 2362: delete($hash{$key}); 2363: } 2364: } 2365: } 2366: } 2367: 2368: sub tmpstore { 2369: my ($storehash,$symb,$namespace,$domain,$stuname) = @_; 2370: 2371: if (!$symb) { 2372: $symb=&symbread(); 2373: if (!$symb) { $symb= $env{'request.url'}; } 2374: } 2375: $symb=escape($symb); 2376: 2377: if (!$namespace) { 2378: # I don't think we would ever want to store this for a course. 2379: # it seems this will only be used if we don't have a course. 2380: #$namespace=$env{'request.course.id'}; 2381: #if (!$namespace) { 2382: $namespace=$env{'request.state'}; 2383: #} 2384: } 2385: $namespace=~s/\//\_/g; 2386: $namespace=~s/\W//g; 2387: if (!$domain) { $domain=$env{'user.domain'}; } 2388: if (!$stuname) { $stuname=$env{'user.name'}; } 2389: if ($domain eq 'public' && $stuname eq 'public') { 2390: $stuname=$ENV{'REMOTE_ADDR'}; 2391: } 2392: my $now=time; 2393: my %hash; 2394: my $path=$perlvar{'lonDaemons'}.'/tmp'; 2395: if (tie(%hash,'GDBM_File', 2396: $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db', 2397: &GDBM_WRCREAT(),0640)) { 2398: $hash{"version:$symb"}++; 2399: my $version=$hash{"version:$symb"}; 2400: my $allkeys=''; 2401: foreach my $key (keys(%$storehash)) { 2402: $allkeys.=$key.':'; 2403: $hash{"$version:$symb:$key"}=&freeze_escape($$storehash{$key}); 2404: } 2405: $hash{"$version:$symb:timestamp"}=$now; 2406: $allkeys.='timestamp'; 2407: $hash{"$version:keys:$symb"}=$allkeys; 2408: if (untie(%hash)) { 2409: return 'ok'; 2410: } else { 2411: return "error:$!"; 2412: } 2413: } else { 2414: return "error:$!"; 2415: } 2416: } 2417: 2418: # -----------------------------------------------------------------Temp Restore 2419: 2420: sub tmprestore { 2421: my ($symb,$namespace,$domain,$stuname) = @_; 2422: 2423: if (!$symb) { 2424: $symb=&symbread(); 2425: if (!$symb) { $symb= $env{'request.url'}; } 2426: } 2427: $symb=escape($symb); 2428: 2429: if (!$namespace) { $namespace=$env{'request.state'}; } 2430: 2431: if (!$domain) { $domain=$env{'user.domain'}; } 2432: if (!$stuname) { $stuname=$env{'user.name'}; } 2433: if ($domain eq 'public' && $stuname eq 'public') { 2434: $stuname=$ENV{'REMOTE_ADDR'}; 2435: } 2436: my %returnhash; 2437: $namespace=~s/\//\_/g; 2438: $namespace=~s/\W//g; 2439: my %hash; 2440: my $path=$perlvar{'lonDaemons'}.'/tmp'; 2441: if (tie(%hash,'GDBM_File', 2442: $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db', 2443: &GDBM_READER(),0640)) { 2444: my $version=$hash{"version:$symb"}; 2445: $returnhash{'version'}=$version; 2446: my $scope; 2447: for ($scope=1;$scope<=$version;$scope++) { 2448: my $vkeys=$hash{"$scope:keys:$symb"}; 2449: my @keys=split(/:/,$vkeys); 2450: my $key; 2451: $returnhash{"$scope:keys"}=$vkeys; 2452: foreach $key (@keys) { 2453: $returnhash{"$scope:$key"}=&thaw_unescape($hash{"$scope:$symb:$key"}); 2454: $returnhash{"$key"}=&thaw_unescape($hash{"$scope:$symb:$key"}); 2455: } 2456: } 2457: if (!(untie(%hash))) { 2458: return "error:$!"; 2459: } 2460: } else { 2461: return "error:$!"; 2462: } 2463: return %returnhash; 2464: } 2465: 2466: # ----------------------------------------------------------------------- Store 2467: 2468: sub store { 2469: my ($storehash,$symb,$namespace,$domain,$stuname) = @_; 2470: my $home=''; 2471: 2472: if ($stuname) { $home=&homeserver($stuname,$domain); } 2473: 2474: $symb=&symbclean($symb); 2475: if (!$symb) { unless ($symb=&symbread()) { return ''; } } 2476: 2477: if (!$domain) { $domain=$env{'user.domain'}; } 2478: if (!$stuname) { $stuname=$env{'user.name'}; } 2479: 2480: &devalidate($symb,$stuname,$domain); 2481: 2482: $symb=escape($symb); 2483: if (!$namespace) { 2484: unless ($namespace=$env{'request.course.id'}) { 2485: return ''; 2486: } 2487: } 2488: if (!$home) { $home=$env{'user.home'}; } 2489: 2490: $$storehash{'ip'}=$ENV{'REMOTE_ADDR'}; 2491: $$storehash{'host'}=$perlvar{'lonHostID'}; 2492: 2493: my $namevalue=''; 2494: foreach (keys %$storehash) { 2495: $namevalue.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&'; 2496: } 2497: $namevalue=~s/\&$//; 2498: &courselog($symb.':'.$stuname.':'.$domain.':STORE:'.$namevalue); 2499: return reply("store:$domain:$stuname:$namespace:$symb:$namevalue","$home"); 2500: } 2501: 2502: # -------------------------------------------------------------- Critical Store 2503: 2504: sub cstore { 2505: my ($storehash,$symb,$namespace,$domain,$stuname) = @_; 2506: my $home=''; 2507: 2508: if ($stuname) { $home=&homeserver($stuname,$domain); } 2509: 2510: $symb=&symbclean($symb); 2511: if (!$symb) { unless ($symb=&symbread()) { return ''; } } 2512: 2513: if (!$domain) { $domain=$env{'user.domain'}; } 2514: if (!$stuname) { $stuname=$env{'user.name'}; } 2515: 2516: &devalidate($symb,$stuname,$domain); 2517: 2518: $symb=escape($symb); 2519: if (!$namespace) { 2520: unless ($namespace=$env{'request.course.id'}) { 2521: return ''; 2522: } 2523: } 2524: if (!$home) { $home=$env{'user.home'}; } 2525: 2526: $$storehash{'ip'}=$ENV{'REMOTE_ADDR'}; 2527: $$storehash{'host'}=$perlvar{'lonHostID'}; 2528: 2529: my $namevalue=''; 2530: foreach (keys %$storehash) { 2531: $namevalue.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&'; 2532: } 2533: $namevalue=~s/\&$//; 2534: &courselog($symb.':'.$stuname.':'.$domain.':CSTORE:'.$namevalue); 2535: return critical 2536: ("store:$domain:$stuname:$namespace:$symb:$namevalue","$home"); 2537: } 2538: 2539: # --------------------------------------------------------------------- Restore 2540: 2541: sub restore { 2542: my ($symb,$namespace,$domain,$stuname) = @_; 2543: my $home=''; 2544: 2545: if ($stuname) { $home=&homeserver($stuname,$domain); } 2546: 2547: if (!$symb) { 2548: unless ($symb=escape(&symbread())) { return ''; } 2549: } else { 2550: $symb=&escape(&symbclean($symb)); 2551: } 2552: if (!$namespace) { 2553: unless ($namespace=$env{'request.course.id'}) { 2554: return ''; 2555: } 2556: } 2557: if (!$domain) { $domain=$env{'user.domain'}; } 2558: if (!$stuname) { $stuname=$env{'user.name'}; } 2559: if (!$home) { $home=$env{'user.home'}; } 2560: my $answer=&reply("restore:$domain:$stuname:$namespace:$symb","$home"); 2561: 2562: my %returnhash=(); 2563: foreach (split(/\&/,$answer)) { 2564: my ($name,$value)=split(/\=/,$_); 2565: $returnhash{&unescape($name)}=&thaw_unescape($value); 2566: } 2567: my $version; 2568: for ($version=1;$version<=$returnhash{'version'};$version++) { 2569: foreach (split(/\:/,$returnhash{$version.':keys'})) { 2570: $returnhash{$_}=$returnhash{$version.':'.$_}; 2571: } 2572: } 2573: return %returnhash; 2574: } 2575: 2576: # ---------------------------------------------------------- Course Description 2577: 2578: sub coursedescription { 2579: my $courseid=shift; 2580: $courseid=~s/^\///; 2581: $courseid=~s/\_/\//g; 2582: my ($cdomain,$cnum)=split(/\//,$courseid); 2583: my $chome=&homeserver($cnum,$cdomain); 2584: my $normalid=$cdomain.'_'.$cnum; 2585: # need to always cache even if we get errors otherwise we keep 2586: # trying and trying and trying to get the course description. 2587: my %envhash=(); 2588: my %returnhash=(); 2589: $envhash{'course.'.$normalid.'.last_cache'}=time; 2590: if ($chome ne 'no_host') { 2591: %returnhash=&dump('environment',$cdomain,$cnum); 2592: if (!exists($returnhash{'con_lost'})) { 2593: $returnhash{'home'}= $chome; 2594: $returnhash{'domain'} = $cdomain; 2595: $returnhash{'num'} = $cnum; 2596: while (my ($name,$value) = each %returnhash) { 2597: $envhash{'course.'.$normalid.'.'.$name}=$value; 2598: } 2599: $returnhash{'url'}=&clutter($returnhash{'url'}); 2600: $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'. 2601: $env{'user.name'}.'_'.$cdomain.'_'.$cnum; 2602: $envhash{'course.'.$normalid.'.home'}=$chome; 2603: $envhash{'course.'.$normalid.'.domain'}=$cdomain; 2604: $envhash{'course.'.$normalid.'.num'}=$cnum; 2605: } 2606: } 2607: &appenv(%envhash); 2608: return %returnhash; 2609: } 2610: 2611: # -------------------------------------------------See if a user is privileged 2612: 2613: sub privileged { 2614: my ($username,$domain)=@_; 2615: my $rolesdump=&reply("dump:$domain:$username:roles", 2616: &homeserver($username,$domain)); 2617: if (($rolesdump eq 'con_lost') || ($rolesdump eq '')) { return 0; } 2618: my $now=time; 2619: if ($rolesdump ne '') { 2620: foreach (split(/&/,$rolesdump)) { 2621: if ($_!~/^rolesdef_/) { 2622: my ($area,$role)=split(/=/,$_); 2623: $area=~s/\_\w\w$//; 2624: my ($trole,$tend,$tstart)=split(/_/,$role); 2625: if (($trole eq 'dc') || ($trole eq 'su')) { 2626: my $active=1; 2627: if ($tend) { 2628: if ($tend<$now) { $active=0; } 2629: } 2630: if ($tstart) { 2631: if ($tstart>$now) { $active=0; } 2632: } 2633: if ($active) { return 1; } 2634: } 2635: } 2636: } 2637: } 2638: return 0; 2639: } 2640: 2641: # -------------------------------------------------------- Get user privileges 2642: 2643: sub rolesinit { 2644: my ($domain,$username,$authhost)=@_; 2645: my $rolesdump=reply("dump:$domain:$username:roles",$authhost); 2646: if (($rolesdump eq 'con_lost') || ($rolesdump eq '')) { return ''; } 2647: my %allroles=(); 2648: my %allgroups=(); 2649: my $now=time; 2650: my $userroles="user.login.time=$now\n"; 2651: my $group_privs; 2652: 2653: if ($rolesdump ne '') { 2654: foreach (split(/&/,$rolesdump)) { 2655: if ($_!~/^rolesdef_/) { 2656: my ($area,$role)=split(/=/,$_); 2657: $area=~s/\_\w\w$//; 2658: my ($trole,$tend,$tstart,$group_privs); 2659: if ($role=~/^cr/) { 2660: if ($role=~m|^(cr/\w+/\w+/[a-zA-Z0-9]+)_(.*)$|) { 2661: ($trole,my $trest)=($role=~m|^(cr/\w+/\w+/[a-zA-Z0-9]+)_(.*)$|); 2662: ($tend,$tstart)=split('_',$trest); 2663: } else { 2664: $trole=$role; 2665: } 2666: } elsif ($role =~ m|^gr/|) { 2667: ($trole,$tend,$tstart) = split(/_/,$role); 2668: ($trole,$group_privs) = split(/\//,$trole); 2669: $group_privs = &unescape($group_privs); 2670: } else { 2671: ($trole,$tend,$tstart)=split(/_/,$role); 2672: } 2673: $userroles.=&set_arearole($trole,$area,$tstart,$tend,$domain,$username); 2674: if (($tend!=0) && ($tend<$now)) { $trole=''; } 2675: if (($tstart!=0) && ($tstart>$now)) { $trole=''; } 2676: if (($area ne '') && ($trole ne '')) { 2677: my $spec=$trole.'.'.$area; 2678: my ($tdummy,$tdomain,$trest)=split(/\//,$area); 2679: if ($trole =~ /^cr\//) { 2680: &custom_roleprivs(\%allroles,$trole,$tdomain,$trest,$spec,$area); 2681: } elsif ($trole eq 'gr') { 2682: &group_roleprivs(\%allgroups,$area,$group_privs,$tend,$tstart); 2683: } else { 2684: &standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area); 2685: } 2686: } 2687: } 2688: } 2689: my ($author,$adv) = &set_userprivs(\$userroles,\%allroles,\%allgroups); 2690: $userroles.='user.adv='.$adv."\n". 2691: 'user.author='.$author."\n"; 2692: $env{'user.adv'}=$adv; 2693: } 2694: return $userroles; 2695: } 2696: 2697: sub set_arearole { 2698: my ($trole,$area,$tstart,$tend,$domain,$username) = @_; 2699: # log the associated role with the area 2700: &userrolelog($trole,$username,$domain,$area,$tstart,$tend); 2701: return 'user.role.'.$trole.'.'.$area.'='.$tstart.'.'.$tend."\n"; 2702: } 2703: 2704: sub custom_roleprivs { 2705: my ($allroles,$trole,$tdomain,$trest,$spec,$area) = @_; 2706: my ($rdummy,$rdomain,$rauthor,$rrole)=split(/\//,$trole); 2707: my $homsvr=homeserver($rauthor,$rdomain); 2708: if ($hostname{$homsvr} ne '') { 2709: my ($rdummy,$roledef)= 2710: &get('roles',["rolesdef_$rrole"],$rdomain,$rauthor); 2711: if (($rdummy ne 'con_lost') && ($roledef ne '')) { 2712: my ($syspriv,$dompriv,$coursepriv)=split(/\_/,$roledef); 2713: if (defined($syspriv)) { 2714: $$allroles{'cm./'}.=':'.$syspriv; 2715: $$allroles{$spec.'./'}.=':'.$syspriv; 2716: } 2717: if ($tdomain ne '') { 2718: if (defined($dompriv)) { 2719: $$allroles{'cm./'.$tdomain.'/'}.=':'.$dompriv; 2720: $$allroles{$spec.'./'.$tdomain.'/'}.=':'.$dompriv; 2721: } 2722: if (($trest ne '') && (defined($coursepriv))) { 2723: $$allroles{'cm.'.$area}.=':'.$coursepriv; 2724: $$allroles{$spec.'.'.$area}.=':'.$coursepriv; 2725: } 2726: } 2727: } 2728: } 2729: } 2730: 2731: sub group_roleprivs { 2732: my ($allgroups,$area,$group_privs,$tend,$tstart) = @_; 2733: my $access = 1; 2734: my $now = time; 2735: if (($tend!=0) && ($tend<$now)) { $access = 0; } 2736: if (($tstart!=0) && ($tstart>$now)) { $access=0; } 2737: if ($access) { 2738: my ($course,$group) = ($area =~ m|(/\w+/\w+)/([^/]+)$|); 2739: $$allgroups{$course}{$group} .=':'.$group_privs; 2740: } 2741: } 2742: 2743: sub standard_roleprivs { 2744: my ($allroles,$trole,$tdomain,$spec,$trest,$area) = @_; 2745: if (defined($pr{$trole.':s'})) { 2746: $$allroles{'cm./'}.=':'.$pr{$trole.':s'}; 2747: $$allroles{$spec.'./'}.=':'.$pr{$trole.':s'}; 2748: } 2749: if ($tdomain ne '') { 2750: if (defined($pr{$trole.':d'})) { 2751: $$allroles{'cm./'.$tdomain.'/'}.=':'.$pr{$trole.':d'}; 2752: $$allroles{$spec.'./'.$tdomain.'/'}.=':'.$pr{$trole.':d'}; 2753: } 2754: if (($trest ne '') && (defined($pr{$trole.':c'}))) { 2755: $$allroles{'cm.'.$area}.=':'.$pr{$trole.':c'}; 2756: $$allroles{$spec.'.'.$area}.=':'.$pr{$trole.':c'}; 2757: } 2758: } 2759: } 2760: 2761: sub set_userprivs { 2762: my ($userroles,$allroles,$allgroups) = @_; 2763: my $author=0; 2764: my $adv=0; 2765: my %grouproles = (); 2766: if (keys(%{$allgroups}) > 0) { 2767: foreach my $role (keys %{$allroles}) { 2768: my ($trole,$area,$sec,$extendedarea); 2769: if ($role =~ m|^(\w+)\.(/\w+/\w+)(/?\w*)|) { 2770: $trole = $1; 2771: $area = $2; 2772: $sec = $3; 2773: $extendedarea = $area.$sec; 2774: if (exists($$allgroups{$area})) { 2775: foreach my $group (keys(%{$$allgroups{$area}})) { 2776: my $spec = $trole.'.'.$extendedarea; 2777: $grouproles{$spec.'.'.$area.'/'.$group} = 2778: $$allgroups{$area}{$group}; 2779: } 2780: } 2781: } 2782: } 2783: } 2784: foreach (keys(%grouproles)) { 2785: $$allroles{$_} = $grouproles{$_}; 2786: } 2787: foreach (keys %{$allroles}) { 2788: my %thesepriv=(); 2789: if (($_=~/^au/) || ($_=~/^ca/)) { $author=1; } 2790: foreach (split(/:/,$$allroles{$_})) { 2791: if ($_ ne '') { 2792: my ($privilege,$restrictions)=split(/&/,$_); 2793: if ($restrictions eq '') { 2794: $thesepriv{$privilege}='F'; 2795: } elsif ($thesepriv{$privilege} ne 'F') { 2796: $thesepriv{$privilege}.=$restrictions; 2797: } 2798: if ($thesepriv{'adv'} eq 'F') { $adv=1; } 2799: } 2800: } 2801: my $thesestr=''; 2802: foreach (keys %thesepriv) { $thesestr.=':'.$_.'&'.$thesepriv{$_}; } 2803: $$userroles.='user.priv.'.$_.'='.$thesestr."\n"; 2804: } 2805: return ($author,$adv); 2806: } 2807: 2808: # --------------------------------------------------------------- get interface 2809: 2810: sub get { 2811: my ($namespace,$storearr,$udomain,$uname)=@_; 2812: my $items=''; 2813: foreach (@$storearr) { 2814: $items.=escape($_).'&'; 2815: } 2816: $items=~s/\&$//; 2817: if (!$udomain) { $udomain=$env{'user.domain'}; } 2818: if (!$uname) { $uname=$env{'user.name'}; } 2819: my $uhome=&homeserver($uname,$udomain); 2820: 2821: my $rep=&reply("get:$udomain:$uname:$namespace:$items",$uhome); 2822: my @pairs=split(/\&/,$rep); 2823: if ( $#pairs==0 && $pairs[0] =~ /^(con_lost|error|no_such_host)/i) { 2824: return @pairs; 2825: } 2826: my %returnhash=(); 2827: my $i=0; 2828: foreach (@$storearr) { 2829: $returnhash{$_}=&thaw_unescape($pairs[$i]); 2830: $i++; 2831: } 2832: return %returnhash; 2833: } 2834: 2835: # --------------------------------------------------------------- del interface 2836: 2837: sub del { 2838: my ($namespace,$storearr,$udomain,$uname)=@_; 2839: my $items=''; 2840: foreach (@$storearr) { 2841: $items.=escape($_).'&'; 2842: } 2843: $items=~s/\&$//; 2844: if (!$udomain) { $udomain=$env{'user.domain'}; } 2845: if (!$uname) { $uname=$env{'user.name'}; } 2846: my $uhome=&homeserver($uname,$udomain); 2847: 2848: return &reply("del:$udomain:$uname:$namespace:$items",$uhome); 2849: } 2850: 2851: # -------------------------------------------------------------- dump interface 2852: 2853: sub dump { 2854: my ($namespace,$udomain,$uname,$regexp,$range)=@_; 2855: if (!$udomain) { $udomain=$env{'user.domain'}; } 2856: if (!$uname) { $uname=$env{'user.name'}; } 2857: my $uhome=&homeserver($uname,$udomain); 2858: if ($regexp) { 2859: $regexp=&escape($regexp); 2860: } else { 2861: $regexp='.'; 2862: } 2863: my $rep=reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome); 2864: my @pairs=split(/\&/,$rep); 2865: my %returnhash=(); 2866: foreach (@pairs) { 2867: my ($key,$value)=split(/=/,$_,2); 2868: $returnhash{unescape($key)}=&thaw_unescape($value); 2869: } 2870: return %returnhash; 2871: } 2872: 2873: # --------------------------------------------------------- dumpstore interface 2874: 2875: sub dumpstore { 2876: my ($namespace,$udomain,$uname,$regexp,$range)=@_; 2877: return &dump($namespace,$udomain,$uname,$regexp,$range); 2878: } 2879: 2880: # -------------------------------------------------------------- keys interface 2881: 2882: sub getkeys { 2883: my ($namespace,$udomain,$uname)=@_; 2884: if (!$udomain) { $udomain=$env{'user.domain'}; } 2885: if (!$uname) { $uname=$env{'user.name'}; } 2886: my $uhome=&homeserver($uname,$udomain); 2887: my $rep=reply("keys:$udomain:$uname:$namespace",$uhome); 2888: my @keyarray=(); 2889: foreach (split(/\&/,$rep)) { 2890: push (@keyarray,&unescape($_)); 2891: } 2892: return @keyarray; 2893: } 2894: 2895: # --------------------------------------------------------------- currentdump 2896: sub currentdump { 2897: my ($courseid,$sdom,$sname)=@_; 2898: $courseid = $env{'request.course.id'} if (! defined($courseid)); 2899: $sdom = $env{'user.domain'} if (! defined($sdom)); 2900: $sname = $env{'user.name'} if (! defined($sname)); 2901: my $uhome = &homeserver($sname,$sdom); 2902: my $rep=reply('currentdump:'.$sdom.':'.$sname.':'.$courseid,$uhome); 2903: return if ($rep =~ /^(error:|no_such_host)/); 2904: # 2905: my %returnhash=(); 2906: # 2907: if ($rep eq "unknown_cmd") { 2908: # an old lond will not know currentdump 2909: # Do a dump and make it look like a currentdump 2910: my @tmp = &dump($courseid,$sdom,$sname,'.'); 2911: return if ($tmp[0] =~ /^(error:|no_such_host)/); 2912: my %hash = @tmp; 2913: @tmp=(); 2914: %returnhash = %{&convert_dump_to_currentdump(\%hash)}; 2915: } else { 2916: my @pairs=split(/\&/,$rep); 2917: foreach (@pairs) { 2918: my ($key,$value)=split(/=/,$_); 2919: my ($symb,$param) = split(/:/,$key); 2920: $returnhash{&unescape($symb)}->{&unescape($param)} = 2921: &thaw_unescape($value); 2922: } 2923: } 2924: return %returnhash; 2925: } 2926: 2927: sub convert_dump_to_currentdump{ 2928: my %hash = %{shift()}; 2929: my %returnhash; 2930: # Code ripped from lond, essentially. The only difference 2931: # here is the unescaping done by lonnet::dump(). Conceivably 2932: # we might run in to problems with parameter names =~ /^v\./ 2933: while (my ($key,$value) = each(%hash)) { 2934: my ($v,$symb,$param) = split(/:/,$key); 2935: next if ($v eq 'version' || $symb eq 'keys'); 2936: next if (exists($returnhash{$symb}) && 2937: exists($returnhash{$symb}->{$param}) && 2938: $returnhash{$symb}->{'v.'.$param} > $v); 2939: $returnhash{$symb}->{$param}=$value; 2940: $returnhash{$symb}->{'v.'.$param}=$v; 2941: } 2942: # 2943: # Remove all of the keys in the hashes which keep track of 2944: # the version of the parameter. 2945: while (my ($symb,$param_hash) = each(%returnhash)) { 2946: # use a foreach because we are going to delete from the hash. 2947: foreach my $key (keys(%$param_hash)) { 2948: delete($param_hash->{$key}) if ($key =~ /^v\./); 2949: } 2950: } 2951: return \%returnhash; 2952: } 2953: 2954: # ------------------------------------------------------ critical inc interface 2955: 2956: sub cinc { 2957: return &inc(@_,'critical'); 2958: } 2959: 2960: # --------------------------------------------------------------- inc interface 2961: 2962: sub inc { 2963: my ($namespace,$store,$udomain,$uname,$critical) = @_; 2964: if (!$udomain) { $udomain=$env{'user.domain'}; } 2965: if (!$uname) { $uname=$env{'user.name'}; } 2966: my $uhome=&homeserver($uname,$udomain); 2967: my $items=''; 2968: if (! ref($store)) { 2969: # got a single value, so use that instead 2970: $items = &escape($store).'=&'; 2971: } elsif (ref($store) eq 'SCALAR') { 2972: $items = &escape($$store).'=&'; 2973: } elsif (ref($store) eq 'ARRAY') { 2974: $items = join('=&',map {&escape($_);} @{$store}); 2975: } elsif (ref($store) eq 'HASH') { 2976: while (my($key,$value) = each(%{$store})) { 2977: $items.= &escape($key).'='.&escape($value).'&'; 2978: } 2979: } 2980: $items=~s/\&$//; 2981: if ($critical) { 2982: return &critical("inc:$udomain:$uname:$namespace:$items",$uhome); 2983: } else { 2984: return &reply("inc:$udomain:$uname:$namespace:$items",$uhome); 2985: } 2986: } 2987: 2988: # --------------------------------------------------------------- put interface 2989: 2990: sub put { 2991: my ($namespace,$storehash,$udomain,$uname)=@_; 2992: if (!$udomain) { $udomain=$env{'user.domain'}; } 2993: if (!$uname) { $uname=$env{'user.name'}; } 2994: my $uhome=&homeserver($uname,$udomain); 2995: my $items=''; 2996: foreach (keys %$storehash) { 2997: $items.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&'; 2998: } 2999: $items=~s/\&$//; 3000: return &reply("put:$udomain:$uname:$namespace:$items",$uhome); 3001: } 3002: 3003: # ------------------------------------------------------------ newput interface 3004: 3005: sub newput { 3006: my ($namespace,$storehash,$udomain,$uname)=@_; 3007: if (!$udomain) { $udomain=$env{'user.domain'}; } 3008: if (!$uname) { $uname=$env{'user.name'}; } 3009: my $uhome=&homeserver($uname,$udomain); 3010: my $items=''; 3011: foreach my $key (keys(%$storehash)) { 3012: $items.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&'; 3013: } 3014: $items=~s/\&$//; 3015: return &reply("newput:$udomain:$uname:$namespace:$items",$uhome); 3016: } 3017: 3018: # --------------------------------------------------------- putstore interface 3019: 3020: sub putstore { 3021: my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_; 3022: if (!$udomain) { $udomain=$env{'user.domain'}; } 3023: if (!$uname) { $uname=$env{'user.name'}; } 3024: my $uhome=&homeserver($uname,$udomain); 3025: my $items=''; 3026: foreach my $key (keys(%$storehash)) { 3027: $items.= &escape($key).'='.&freeze_escape($storehash->{$key}).'&'; 3028: } 3029: $items=~s/\&$//; 3030: my $esc_symb=&escape($symb); 3031: my $esc_v=&escape($version); 3032: my $reply = 3033: &reply("putstore:$udomain:$uname:$namespace:$esc_symb:$esc_v:$items", 3034: $uhome); 3035: if ($reply eq 'unknown_cmd') { 3036: # gfall back to way things use to be done 3037: return &old_putstore($namespace,$symb,$version,$storehash,$udomain, 3038: $uname); 3039: } 3040: return $reply; 3041: } 3042: 3043: sub old_putstore { 3044: my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_; 3045: if (!$udomain) { $udomain=$env{'user.domain'}; } 3046: if (!$uname) { $uname=$env{'user.name'}; } 3047: my $uhome=&homeserver($uname,$udomain); 3048: my %newstorehash; 3049: foreach (keys %$storehash) { 3050: my $key = $version.':'.&escape($symb).':'.$_; 3051: $newstorehash{$key} = $storehash->{$_}; 3052: } 3053: my $items=''; 3054: my %allitems = (); 3055: foreach (keys %newstorehash) { 3056: if ($_ =~ m/^([^\:]+):([^\:]+):([^\:]+)$/) { 3057: my $key = $1.':keys:'.$2; 3058: $allitems{$key} .= $3.':'; 3059: } 3060: $items.=$_.'='.&freeze_escape($newstorehash{$_}).'&'; 3061: } 3062: foreach (keys %allitems) { 3063: $allitems{$_} =~ s/\:$//; 3064: $items.= $_.'='.$allitems{$_}.'&'; 3065: } 3066: $items=~s/\&$//; 3067: return &reply("put:$udomain:$uname:$namespace:$items",$uhome); 3068: } 3069: 3070: # ------------------------------------------------------ critical put interface 3071: 3072: sub cput { 3073: my ($namespace,$storehash,$udomain,$uname)=@_; 3074: if (!$udomain) { $udomain=$env{'user.domain'}; } 3075: if (!$uname) { $uname=$env{'user.name'}; } 3076: my $uhome=&homeserver($uname,$udomain); 3077: my $items=''; 3078: foreach (keys %$storehash) { 3079: $items.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&'; 3080: } 3081: $items=~s/\&$//; 3082: return &critical("put:$udomain:$uname:$namespace:$items",$uhome); 3083: } 3084: 3085: # -------------------------------------------------------------- eget interface 3086: 3087: sub eget { 3088: my ($namespace,$storearr,$udomain,$uname)=@_; 3089: my $items=''; 3090: foreach (@$storearr) { 3091: $items.=escape($_).'&'; 3092: } 3093: $items=~s/\&$//; 3094: if (!$udomain) { $udomain=$env{'user.domain'}; } 3095: if (!$uname) { $uname=$env{'user.name'}; } 3096: my $uhome=&homeserver($uname,$udomain); 3097: my $rep=&reply("eget:$udomain:$uname:$namespace:$items",$uhome); 3098: my @pairs=split(/\&/,$rep); 3099: my %returnhash=(); 3100: my $i=0; 3101: foreach (@$storearr) { 3102: $returnhash{$_}=&thaw_unescape($pairs[$i]); 3103: $i++; 3104: } 3105: return %returnhash; 3106: } 3107: 3108: # ------------------------------------------------------------ tmpput interface 3109: sub tmpput { 3110: my ($storehash,$server)=@_; 3111: my $items=''; 3112: foreach (keys(%$storehash)) { 3113: $items.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&'; 3114: } 3115: $items=~s/\&$//; 3116: return &reply("tmpput:$items",$server); 3117: } 3118: 3119: # ------------------------------------------------------------ tmpget interface 3120: sub tmpget { 3121: my ($token,$server)=@_; 3122: if (!defined($server)) { $server = $perlvar{'lonHostID'}; } 3123: my $rep=&reply("tmpget:$token",$server); 3124: my %returnhash; 3125: foreach my $item (split(/\&/,$rep)) { 3126: my ($key,$value)=split(/=/,$item); 3127: $returnhash{&unescape($key)}=&thaw_unescape($value); 3128: } 3129: return %returnhash; 3130: } 3131: 3132: # ------------------------------------------------------------ tmpget interface 3133: sub tmpdel { 3134: my ($token,$server)=@_; 3135: if (!defined($server)) { $server = $perlvar{'lonHostID'}; } 3136: return &reply("tmpdel:$token",$server); 3137: } 3138: 3139: # ---------------------------------------------- Custom access rule evaluation 3140: 3141: sub customaccess { 3142: my ($priv,$uri)=@_; 3143: my ($urole,$urealm)=split(/\./,$env{'request.role'}); 3144: $urealm=~s/^\W//; 3145: my ($udom,$ucrs,$usec)=split(/\//,$urealm); 3146: my $access=0; 3147: foreach (split(/\s*\,\s*/,&metadata($uri,'rule_rights'))) { 3148: my ($effect,$realm,$role)=split(/\:/,$_); 3149: if ($role) { 3150: if ($role ne $urole) { next; } 3151: } 3152: foreach (split(/\s*\,\s*/,$realm)) { 3153: my ($tdom,$tcrs,$tsec)=split(/\_/,$_); 3154: if ($tdom) { 3155: if ($tdom ne $udom) { next; } 3156: } 3157: if ($tcrs) { 3158: if ($tcrs ne $ucrs) { next; } 3159: } 3160: if ($tsec) { 3161: if ($tsec ne $usec) { next; } 3162: } 3163: $access=($effect eq 'allow'); 3164: last; 3165: } 3166: if ($realm eq '' && $role eq '') { 3167: $access=($effect eq 'allow'); 3168: } 3169: } 3170: return $access; 3171: } 3172: 3173: # ------------------------------------------------- Check for a user privilege 3174: 3175: sub allowed { 3176: my ($priv,$uri,$symb)=@_; 3177: my $ver_orguri=$uri; 3178: $uri=&deversion($uri); 3179: my $orguri=$uri; 3180: $uri=&declutter($uri); 3181: 3182: if (defined($env{'allowed.'.$priv})) { return $env{'allowed.'.$priv}; } 3183: # Free bre access to adm and meta resources 3184: if (((($uri=~/^adm\//) && ($uri !~ m|/bulletinboard$|)) 3185: || ($uri=~/\.meta$/)) && ($priv eq 'bre')) { 3186: return 'F'; 3187: } 3188: 3189: # Free bre access to user's own portfolio contents 3190: my ($space,$domain,$name,@dir)=split('/',$uri); 3191: if (($space=~/^(uploaded|editupload)$/) && ($env{'user.name'} eq $name) && 3192: ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir[0])) { 3193: return 'F'; 3194: } 3195: 3196: # bre access to group if user has rgf priv for this group and course. 3197: if (($space=~/^(uploaded|editupload)$/) && ($dir[0] eq 'groups') 3198: && ($dir[2] eq 'portfolio') && ($priv eq 'bre')) { 3199: if (exists($env{'request.course.id'})) { 3200: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; 3201: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; 3202: if (($domain eq $cdom) && ($name eq $cnum)) { 3203: my $courseprivid=$env{'request.course.id'}; 3204: $courseprivid=~s/\_/\//; 3205: if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid 3206: .'/'.$dir[1]} =~/rgf\&([^\:]*)/) { 3207: return $1; 3208: } 3209: } 3210: } 3211: } 3212: 3213: # Free bre to public access 3214: 3215: if ($priv eq 'bre') { 3216: my $copyright=&metadata($uri,'copyright'); 3217: if (($copyright eq 'public') && (!$env{'request.course.id'})) { 3218: return 'F'; 3219: } 3220: if ($copyright eq 'priv') { 3221: $uri=~/([^\/]+)\/([^\/]+)\//; 3222: unless (($env{'user.name'} eq $2) && ($env{'user.domain'} eq $1)) { 3223: return ''; 3224: } 3225: } 3226: if ($copyright eq 'domain') { 3227: $uri=~/([^\/]+)\/([^\/]+)\//; 3228: unless (($env{'user.domain'} eq $1) || 3229: ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $1)) { 3230: return ''; 3231: } 3232: } 3233: if ($env{'request.role'}=~ /li\.\//) { 3234: # Library role, so allow browsing of resources in this domain. 3235: return 'F'; 3236: } 3237: if ($copyright eq 'custom') { 3238: unless (&customaccess($priv,$uri)) { return ''; } 3239: } 3240: } 3241: # Domain coordinator is trying to create a course 3242: if (($priv eq 'ccc') && ($env{'request.role'} =~ /^dc\./)) { 3243: # uri is the requested domain in this case. 3244: # comparison to 'request.role.domain' shows if the user has selected 3245: # a role of dc for the domain in question. 3246: return 'F' if ($uri eq $env{'request.role.domain'}); 3247: } 3248: 3249: my $thisallowed=''; 3250: my $statecond=0; 3251: my $courseprivid=''; 3252: 3253: # Course 3254: 3255: if ($env{'user.priv.'.$env{'request.role'}.'./'}=~/\Q$priv\E\&([^\:]*)/) { 3256: $thisallowed.=$1; 3257: } 3258: 3259: # Domain 3260: 3261: if ($env{'user.priv.'.$env{'request.role'}.'./'.(split(/\//,$uri))[0].'/'} 3262: =~/\Q$priv\E\&([^\:]*)/) { 3263: $thisallowed.=$1; 3264: } 3265: 3266: # Course: uri itself is a course 3267: my $courseuri=$uri; 3268: $courseuri=~s/\_(\d)/\/$1/; 3269: $courseuri=~s/^([^\/])/\/$1/; 3270: 3271: if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseuri} 3272: =~/\Q$priv\E\&([^\:]*)/) { 3273: $thisallowed.=$1; 3274: } 3275: 3276: # Group: uri itself is a group 3277: my $groupuri=$uri; 3278: $groupuri=~s/^([^\/])/\/$1/; 3279: if ($env{'user.priv.'.$env{'request.role'}.'.'.$groupuri} 3280: =~/\Q$priv\E\&([^\:]*)/) { 3281: $thisallowed.=$1; 3282: } 3283: 3284: # URI is an uploaded document for this course, default permissions don't matter 3285: # not allowing 'edit' access (editupload) to uploaded course docs 3286: if (($priv eq 'bre') && ($uri=~m|^uploaded/|)) { 3287: $thisallowed=''; 3288: my ($match)=&is_on_map($uri); 3289: if ($match) { 3290: if ($env{'user.priv.'.$env{'request.role'}.'./'} 3291: =~/\Q$priv\E\&([^\:]*)/) { 3292: $thisallowed.=$1; 3293: } 3294: } else { 3295: my $refuri = $env{'httpref.'.$orguri} || $env{'httpref.'.$ver_orguri}; 3296: if ($refuri) { 3297: if ($refuri =~ m|^/adm/|) { 3298: $thisallowed='F'; 3299: } else { 3300: $refuri=&declutter($refuri); 3301: my ($match) = &is_on_map($refuri); 3302: if ($match) { 3303: $thisallowed='F'; 3304: } 3305: } 3306: } 3307: } 3308: } 3309: 3310: # Full access at system, domain or course-wide level? Exit. 3311: 3312: if ($thisallowed=~/F/) { 3313: return 'F'; 3314: } 3315: 3316: # If this is generating or modifying users, exit with special codes 3317: 3318: if (':csu:cdc:ccc:cin:cta:cep:ccr:cst:cad:cli:cau:cdg:cca:caa:'=~/\:\Q$priv\E\:/) { 3319: if (($priv eq 'cca') || ($priv eq 'caa')) { 3320: my ($audom,$auname)=split('/',$uri); 3321: # no author name given, so this just checks on the general right to make a co-author in this domain 3322: unless ($auname) { return $thisallowed; } 3323: # an author name is given, so we are about to actually make a co-author for a certain account 3324: if (($auname ne $env{'user.name'} && $env{'request.role'} !~ /^dc\./) || 3325: (($audom ne $env{'user.domain'} && $env{'request.role'} !~ /^dc\./) && 3326: ($audom ne $env{'request.role.domain'}))) { return ''; } 3327: } 3328: return $thisallowed; 3329: } 3330: # 3331: # Gathered so far: system, domain and course wide privileges 3332: # 3333: # Course: See if uri or referer is an individual resource that is part of 3334: # the course 3335: 3336: if ($env{'request.course.id'}) { 3337: 3338: $courseprivid=$env{'request.course.id'}; 3339: if ($env{'request.course.sec'}) { 3340: $courseprivid.='/'.$env{'request.course.sec'}; 3341: } 3342: $courseprivid=~s/\_/\//; 3343: my $checkreferer=1; 3344: my ($match,$cond)=&is_on_map($uri); 3345: if ($match) { 3346: $statecond=$cond; 3347: if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid} 3348: =~/\Q$priv\E\&([^\:]*)/) { 3349: $thisallowed.=$1; 3350: $checkreferer=0; 3351: } 3352: } 3353: 3354: if ($checkreferer) { 3355: my $refuri=$env{'httpref.'.$orguri}; 3356: unless ($refuri) { 3357: foreach (keys %env) { 3358: if ($_=~/^httpref\..*\*/) { 3359: my $pattern=$_; 3360: $pattern=~s/^httpref\.\/res\///; 3361: $pattern=~s/\*/\[\^\/\]\+/g; 3362: $pattern=~s/\//\\\//g; 3363: if ($orguri=~/$pattern/) { 3364: $refuri=$env{$_}; 3365: } 3366: } 3367: } 3368: } 3369: 3370: if ($refuri) { 3371: $refuri=&declutter($refuri); 3372: my ($match,$cond)=&is_on_map($refuri); 3373: if ($match) { 3374: my $refstatecond=$cond; 3375: if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid} 3376: =~/\Q$priv\E\&([^\:]*)/) { 3377: $thisallowed.=$1; 3378: $uri=$refuri; 3379: $statecond=$refstatecond; 3380: } 3381: } 3382: } 3383: } 3384: } 3385: 3386: # 3387: # Gathered now: all privileges that could apply, and condition number 3388: # 3389: # 3390: # Full or no access? 3391: # 3392: 3393: if ($thisallowed=~/F/) { 3394: return 'F'; 3395: } 3396: 3397: unless ($thisallowed) { 3398: return ''; 3399: } 3400: 3401: # Restrictions exist, deal with them 3402: # 3403: # C:according to course preferences 3404: # R:according to resource settings 3405: # L:unless locked 3406: # X:according to user session state 3407: # 3408: 3409: # Possibly locked functionality, check all courses 3410: # Locks might take effect only after 10 minutes cache expiration for other 3411: # courses, and 2 minutes for current course 3412: 3413: my $envkey; 3414: if ($thisallowed=~/L/) { 3415: foreach $envkey (keys %env) { 3416: if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) { 3417: my $courseid=$2; 3418: my $roleid=$1.'.'.$2; 3419: $courseid=~s/^\///; 3420: my $expiretime=600; 3421: if ($env{'request.role'} eq $roleid) { 3422: $expiretime=120; 3423: } 3424: my ($cdom,$cnum,$csec)=split(/\//,$courseid); 3425: my $prefix='course.'.$cdom.'_'.$cnum.'.'; 3426: if ((time-$env{$prefix.'last_cache'})>$expiretime) { 3427: &coursedescription($courseid); 3428: } 3429: if (($env{$prefix.'res.'.$uri.'.lock.sections'}=~/\,\Q$csec\E\,/) 3430: || ($env{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) { 3431: if ($env{$prefix.'res.'.$uri.'.lock.expire'}>time) { 3432: &log($env{'user.domain'},$env{'user.name'}, 3433: $env{'user.home'}, 3434: 'Locked by res: '.$priv.' for '.$uri.' due to '. 3435: $cdom.'/'.$cnum.'/'.$csec.' expire '. 3436: $env{$prefix.'priv.'.$priv.'.lock.expire'}); 3437: return ''; 3438: } 3439: } 3440: if (($env{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,\Q$csec\E\,/) 3441: || ($env{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) { 3442: if ($env{'priv.'.$priv.'.lock.expire'}>time) { 3443: &log($env{'user.domain'},$env{'user.name'}, 3444: $env{'user.home'}, 3445: 'Locked by priv: '.$priv.' for '.$uri.' due to '. 3446: $cdom.'/'.$cnum.'/'.$csec.' expire '. 3447: $env{$prefix.'priv.'.$priv.'.lock.expire'}); 3448: return ''; 3449: } 3450: } 3451: } 3452: } 3453: } 3454: 3455: # 3456: # Rest of the restrictions depend on selected course 3457: # 3458: 3459: unless ($env{'request.course.id'}) { 3460: return '1'; 3461: } 3462: 3463: # 3464: # Now user is definitely in a course 3465: # 3466: 3467: 3468: # Course preferences 3469: 3470: if ($thisallowed=~/C/) { 3471: my $rolecode=(split(/\./,$env{'request.role'}))[0]; 3472: my $unamedom=$env{'user.name'}.':'.$env{'user.domain'}; 3473: if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'} 3474: =~/\Q$rolecode\E/) { 3475: if ($priv ne 'pch') { 3476: &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'. 3477: 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '. 3478: $env{'request.course.id'}); 3479: } 3480: return ''; 3481: } 3482: 3483: if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'} 3484: =~/\Q$unamedom\E/) { 3485: if ($priv ne 'pch') { 3486: &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}. 3487: 'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '. 3488: $env{'request.course.id'}); 3489: } 3490: return ''; 3491: } 3492: } 3493: 3494: # Resource preferences 3495: 3496: if ($thisallowed=~/R/) { 3497: my $rolecode=(split(/\./,$env{'request.role'}))[0]; 3498: if (&metadata($uri,'roledeny')=~/\Q$rolecode\E/) { 3499: if ($priv ne 'pch') { 3500: &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'. 3501: 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode); 3502: } 3503: return ''; 3504: } 3505: } 3506: 3507: # Restricted by state or randomout? 3508: 3509: if ($thisallowed=~/X/) { 3510: if ($env{'acc.randomout'}) { 3511: if (!$symb) { $symb=&symbread($uri,1); } 3512: if (($symb) && ($env{'acc.randomout'}=~/\&\Q$symb\E\&/)) { 3513: return ''; 3514: } 3515: } 3516: if (&condval($statecond)) { 3517: return '2'; 3518: } else { 3519: return ''; 3520: } 3521: } 3522: 3523: return 'F'; 3524: } 3525: 3526: sub split_uri_for_cond { 3527: my $uri=&deversion(&declutter(shift)); 3528: my @uriparts=split(/\//,$uri); 3529: my $filename=pop(@uriparts); 3530: my $pathname=join('/',@uriparts); 3531: return ($pathname,$filename); 3532: } 3533: # --------------------------------------------------- Is a resource on the map? 3534: 3535: sub is_on_map { 3536: my ($pathname,$filename) = &split_uri_for_cond(shift); 3537: #Trying to find the conditional for the file 3538: my $match=($env{'acc.res.'.$env{'request.course.id'}.'.'.$pathname}=~ 3539: /\&\Q$filename\E\:([\d\|]+)\&/); 3540: if ($match) { 3541: return (1,$1); 3542: } else { 3543: return (0,0); 3544: } 3545: } 3546: 3547: # --------------------------------------------------------- Get symb from alias 3548: 3549: sub get_symb_from_alias { 3550: my $symb=shift; 3551: my ($map,$resid,$url)=&decode_symb($symb); 3552: # Already is a symb 3553: if ($url) { return $symb; } 3554: # Must be an alias 3555: my $aliassymb=''; 3556: my %bighash; 3557: if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db', 3558: &GDBM_READER(),0640)) { 3559: my $rid=$bighash{'mapalias_'.$symb}; 3560: if ($rid) { 3561: my ($mapid,$resid)=split(/\./,$rid); 3562: $aliassymb=&encode_symb($bighash{'map_id_'.$mapid}, 3563: $resid,$bighash{'src_'.$rid}); 3564: } 3565: untie %bighash; 3566: } 3567: return $aliassymb; 3568: } 3569: 3570: # ----------------------------------------------------------------- Define Role 3571: 3572: sub definerole { 3573: if (allowed('mcr','/')) { 3574: my ($rolename,$sysrole,$domrole,$courole)=@_; 3575: foreach (split(':',$sysrole)) { 3576: my ($crole,$cqual)=split(/\&/,$_); 3577: if ($pr{'cr:s'}!~/\Q$crole\E/) { return "refused:s:$crole"; } 3578: if ($pr{'cr:s'}=~/\Q$crole\E\&/) { 3579: if ($pr{'cr:s'}!~/\Q$crole\E\&\w*\Q$cqual\E/) { 3580: return "refused:s:$crole&$cqual"; 3581: } 3582: } 3583: } 3584: foreach (split(':',$domrole)) { 3585: my ($crole,$cqual)=split(/\&/,$_); 3586: if ($pr{'cr:d'}!~/\Q$crole\E/) { return "refused:d:$crole"; } 3587: if ($pr{'cr:d'}=~/\Q$crole\E\&/) { 3588: if ($pr{'cr:d'}!~/\Q$crole\W\&\w*\Q$cqual\E/) { 3589: return "refused:d:$crole&$cqual"; 3590: } 3591: } 3592: } 3593: foreach (split(':',$courole)) { 3594: my ($crole,$cqual)=split(/\&/,$_); 3595: if ($pr{'cr:c'}!~/\Q$crole\E/) { return "refused:c:$crole"; } 3596: if ($pr{'cr:c'}=~/\Q$crole\E\&/) { 3597: if ($pr{'cr:c'}!~/\Q$crole\E\&\w*\Q$cqual\E/) { 3598: return "refused:c:$crole&$cqual"; 3599: } 3600: } 3601: } 3602: my $command="encrypt:rolesput:$env{'user.domain'}:$env{'user.name'}:". 3603: "$env{'user.domain'}:$env{'user.name'}:". 3604: "rolesdef_$rolename=". 3605: escape($sysrole.'_'.$domrole.'_'.$courole); 3606: return reply($command,$env{'user.home'}); 3607: } else { 3608: return 'refused'; 3609: } 3610: } 3611: 3612: # ---------------- Make a metadata query against the network of library servers 3613: 3614: sub metadata_query { 3615: my ($query,$custom,$customshow,$server_array)=@_; 3616: my %rhash; 3617: my @server_list = (defined($server_array) ? @$server_array 3618: : keys(%libserv) ); 3619: for my $server (@server_list) { 3620: unless ($custom or $customshow) { 3621: my $reply=&reply("querysend:".&escape($query),$server); 3622: $rhash{$server}=$reply; 3623: } 3624: else { 3625: my $reply=&reply("querysend:".&escape($query).':'. 3626: &escape($custom).':'.&escape($customshow), 3627: $server); 3628: $rhash{$server}=$reply; 3629: } 3630: } 3631: return \%rhash; 3632: } 3633: 3634: # ----------------------------------------- Send log queries and wait for reply 3635: 3636: sub log_query { 3637: my ($uname,$udom,$query,%filters)=@_; 3638: my $uhome=&homeserver($uname,$udom); 3639: if ($uhome eq 'no_host') { return 'error: no_host'; } 3640: my $uhost=$hostname{$uhome}; 3641: my $command=&escape(join(':',map{$_.'='.$filters{$_}} keys %filters)); 3642: my $queryid=&reply("querysend:".$query.':'.$udom.':'.$uname.':'.$command, 3643: $uhome); 3644: unless ($queryid=~/^\Q$uhost\E\_/) { return 'error: '.$queryid; } 3645: return get_query_reply($queryid); 3646: } 3647: 3648: # ------- Request retrieval of institutional classlists for course(s) 3649: 3650: sub fetch_enrollment_query { 3651: my ($context,$affiliatesref,$replyref,$dom,$cnum) = @_; 3652: my $homeserver; 3653: my $maxtries = 1; 3654: if ($context eq 'automated') { 3655: $homeserver = $perlvar{'lonHostID'}; 3656: $maxtries = 10; # will wait for up to 2000s for retrieval of classlist data before timeout 3657: } else { 3658: $homeserver = &homeserver($cnum,$dom); 3659: } 3660: my $host=$hostname{$homeserver}; 3661: my $cmd = ''; 3662: foreach (keys %{$affiliatesref}) { 3663: $cmd .= $_.'='.join(",",@{$$affiliatesref{$_}}).'%%'; 3664: } 3665: $cmd =~ s/%%$//; 3666: $cmd = &escape($cmd); 3667: my $query = 'fetchenrollment'; 3668: my $queryid=&reply("querysend:".$query.':'.$dom.':'.$env{'user.name'}.':'.$cmd,$homeserver); 3669: unless ($queryid=~/^\Q$host\E\_/) { 3670: &logthis('fetch_enrollment_query: invalid queryid: '.$queryid.' for host: '.$host.' and homeserver: '.$homeserver.' context: '.$context.' '.$cnum); 3671: return 'error: '.$queryid; 3672: } 3673: my $reply = &get_query_reply($queryid); 3674: my $tries = 1; 3675: while (($reply=~/^timeout/) && ($tries < $maxtries)) { 3676: $reply = &get_query_reply($queryid); 3677: $tries ++; 3678: } 3679: if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) { 3680: &logthis('fetch_enrollment_query error: '.$reply.' for '.$dom.' '.$env{'user.name'}.' for '.$queryid.' context: '.$context.' '.$cnum.' maxtries: '.$maxtries.' tries: '.$tries); 3681: } else { 3682: my @responses = split/:/,$reply; 3683: if ($homeserver eq $perlvar{'lonHostID'}) { 3684: foreach (@responses) { 3685: my ($key,$value) = split/=/,$_; 3686: $$replyref{$key} = $value; 3687: } 3688: } else { 3689: my $pathname = $perlvar{'lonDaemons'}.'/tmp'; 3690: foreach (@responses) { 3691: my ($key,$value) = split/=/,$_; 3692: $$replyref{$key} = $value; 3693: if ($value > 0) { 3694: foreach (@{$$affiliatesref{$key}}) { 3695: my $filename = $dom.'_'.$key.'_'.$_.'_classlist.xml'; 3696: my $destname = $pathname.'/'.$filename; 3697: my $xml_classlist = &reply("autoretrieve:".$filename,$homeserver); 3698: if ($xml_classlist =~ /^error/) { 3699: &logthis('fetch_enrollment_query - autoretrieve error: '.$xml_classlist.' for '.$filename.' from server: '.$homeserver.' '.$context.' '.$cnum); 3700: } else { 3701: if ( open(FILE,">$destname") ) { 3702: print FILE &unescape($xml_classlist); 3703: close(FILE); 3704: } else { 3705: &logthis('fetch_enrollment_query - error opening classlist file '.$destname.' '.$context.' '.$cnum); 3706: } 3707: } 3708: } 3709: } 3710: } 3711: } 3712: return 'ok'; 3713: } 3714: return 'error'; 3715: } 3716: 3717: sub get_query_reply { 3718: my $queryid=shift; 3719: my $replyfile=$perlvar{'lonDaemons'}.'/tmp/'.$queryid; 3720: my $reply=''; 3721: for (1..100) { 3722: sleep 2; 3723: if (-e $replyfile.'.end') { 3724: if (open(my $fh,$replyfile)) { 3725: $reply.=<$fh>; 3726: close($fh); 3727: } else { return 'error: reply_file_error'; } 3728: return &unescape($reply); 3729: } 3730: } 3731: return 'timeout:'.$queryid; 3732: } 3733: 3734: sub courselog_query { 3735: # 3736: # possible filters: 3737: # url: url or symb 3738: # username 3739: # domain 3740: # action: view, submit, grade 3741: # start: timestamp 3742: # end: timestamp 3743: # 3744: my (%filters)=@_; 3745: unless ($env{'request.course.id'}) { return 'no_course'; } 3746: if ($filters{'url'}) { 3747: $filters{'url'}=&symbclean(&declutter($filters{'url'})); 3748: $filters{'url'}=~s/\.(\w+)$/(\\.\\d+)*\\.$1/; 3749: $filters{'url'}=~s/\.(\w+)\_\_\_/(\\.\\d+)*\\.$1/; 3750: } 3751: my $cname=$env{'course.'.$env{'request.course.id'}.'.num'}; 3752: my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; 3753: return &log_query($cname,$cdom,'courselog',%filters); 3754: } 3755: 3756: sub userlog_query { 3757: my ($uname,$udom,%filters)=@_; 3758: return &log_query($uname,$udom,'userlog',%filters); 3759: } 3760: 3761: #--------- Call auto-enrollment subs in localenroll.pm for homeserver for course 3762: 3763: sub auto_run { 3764: my ($cnum,$cdom) = @_; 3765: my $homeserver = &homeserver($cnum,$cdom); 3766: my $response = &reply('autorun:'.$cdom,$homeserver); 3767: return $response; 3768: } 3769: 3770: sub auto_get_sections { 3771: my ($cnum,$cdom,$inst_coursecode) = @_; 3772: my $homeserver = &homeserver($cnum,$cdom); 3773: my @secs = (); 3774: my $response=&unescape(&reply('autogetsections:'.$inst_coursecode.':'.$cdom,$homeserver)); 3775: unless ($response eq 'refused') { 3776: @secs = split/:/,$response; 3777: } 3778: return @secs; 3779: } 3780: 3781: sub auto_new_course { 3782: my ($cnum,$cdom,$inst_course_id,$owner) = @_; 3783: my $homeserver = &homeserver($cnum,$cdom); 3784: my $response=&unescape(&reply('autonewcourse:'.$inst_course_id.':'.$owner.':'.$cdom,$homeserver)); 3785: return $response; 3786: } 3787: 3788: sub auto_validate_courseID { 3789: my ($cnum,$cdom,$inst_course_id) = @_; 3790: my $homeserver = &homeserver($cnum,$cdom); 3791: my $response=&unescape(&reply('autovalidatecourse:'.$inst_course_id.':'.$cdom,$homeserver)); 3792: return $response; 3793: } 3794: 3795: sub auto_create_password { 3796: my ($cnum,$cdom,$authparam) = @_; 3797: my $homeserver = &homeserver($cnum,$cdom); 3798: my $create_passwd = 0; 3799: my $authchk = ''; 3800: my $response=&unescape(&reply('autocreatepassword:'.$authparam.':'.$cdom,$homeserver)); 3801: if ($response eq 'refused') { 3802: $authchk = 'refused'; 3803: } else { 3804: ($authparam,$create_passwd,$authchk) = split/:/,$response; 3805: } 3806: return ($authparam,$create_passwd,$authchk); 3807: } 3808: 3809: sub auto_photo_permission { 3810: my ($cnum,$cdom,$students) = @_; 3811: my $homeserver = &homeserver($cnum,$cdom); 3812: my ($outcome,$perm_reqd,$conditions) = 3813: split(/:/,&unescape(&reply('autophotopermission:'.$cdom,$homeserver)),3); 3814: if ($outcome =~ /^(con_lost|unknown_cmd|no_such_host)$/) { 3815: return (undef,undef); 3816: } 3817: return ($outcome,$perm_reqd,$conditions); 3818: } 3819: 3820: sub auto_checkphotos { 3821: my ($uname,$udom,$pid) = @_; 3822: my $homeserver = &homeserver($uname,$udom); 3823: my ($result,$resulttype); 3824: my $outcome = &unescape(&reply('autophotocheck:'.&escape($udom).':'. 3825: &escape($uname).':'.&escape($pid), 3826: $homeserver)); 3827: if ($outcome =~ /^(con_lost|unknown_cmd|no_such_host)$/) { 3828: return (undef,undef); 3829: } 3830: if ($outcome) { 3831: ($result,$resulttype) = split(/:/,$outcome); 3832: } 3833: return ($result,$resulttype); 3834: } 3835: 3836: sub auto_photochoice { 3837: my ($cnum,$cdom) = @_; 3838: my $homeserver = &homeserver($cnum,$cdom); 3839: my ($update,$comment) = split(/:/,&unescape(&reply('autophotochoice:'. 3840: &escape($cdom), 3841: $homeserver))); 3842: if ($update =~ /^(con_lost|unknown_cmd|no_such_host)$/) { 3843: return (undef,undef); 3844: } 3845: return ($update,$comment); 3846: } 3847: 3848: sub auto_photoupdate { 3849: my ($affiliatesref,$dom,$cnum,$photo) = @_; 3850: my $homeserver = &homeserver($cnum,$dom); 3851: my $host=$hostname{$homeserver}; 3852: my $cmd = ''; 3853: my $maxtries = 1; 3854: foreach (keys %{$affiliatesref}) { 3855: $cmd .= $_.'='.join(",",@{$$affiliatesref{$_}}).'%%'; 3856: } 3857: $cmd =~ s/%%$//; 3858: $cmd = &escape($cmd); 3859: my $query = 'institutionalphotos'; 3860: my $queryid=&reply("querysend:".$query.':'.$dom.':'.$cnum.':'.$cmd,$homeserver); 3861: unless ($queryid=~/^\Q$host\E\_/) { 3862: &logthis('institutionalphotos: invalid queryid: '.$queryid.' for host: '.$host.' and homeserver: '.$homeserver.' and course: '.$cnum); 3863: return 'error: '.$queryid; 3864: } 3865: my $reply = &get_query_reply($queryid); 3866: my $tries = 1; 3867: while (($reply=~/^timeout/) && ($tries < $maxtries)) { 3868: $reply = &get_query_reply($queryid); 3869: $tries ++; 3870: } 3871: if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) { 3872: &logthis('institutionalphotos error: '.$reply.' for '.$dom.' '.$env{'user.name'}.' for '.$queryid.' course: '.$cnum.' maxtries: '.$maxtries.' tries: '.$tries); 3873: } else { 3874: my @responses = split(/:/,$reply); 3875: my $outcome = shift(@responses); 3876: foreach my $item (@responses) { 3877: my ($key,$value) = split(/=/,$item); 3878: $$photo{$key} = $value; 3879: } 3880: return $outcome; 3881: } 3882: return 'error'; 3883: } 3884: 3885: sub auto_instcode_format { 3886: my ($caller,$codedom,$instcodes,$codes,$codetitles,$cat_titles,$cat_order) = @_; 3887: my $courses = ''; 3888: my $homeserver; 3889: if ($caller eq 'global') { 3890: foreach my $tryserver (keys %libserv) { 3891: if ($hostdom{$tryserver} eq $codedom) { 3892: $homeserver = $tryserver; 3893: last; 3894: } 3895: } 3896: if (($env{'user.name'}) && ($env{'user.domain'} eq $codedom)) { 3897: $homeserver = &homeserver($env{'user.name'},$codedom); 3898: } 3899: } else { 3900: $homeserver = &homeserver($caller,$codedom); 3901: } 3902: foreach (keys %{$instcodes}) { 3903: $courses .= &escape($_).'='.&escape($$instcodes{$_}).'&'; 3904: } 3905: chop($courses); 3906: my $response=&reply('autoinstcodeformat:'.$codedom.':'.$courses,$homeserver); 3907: unless ($response =~ /(con_lost|error|no_such_host|refused)/) { 3908: my ($codes_str,$codetitles_str,$cat_titles_str,$cat_order_str) = split/:/,$response; 3909: %{$codes} = &str2hash($codes_str); 3910: @{$codetitles} = &str2array($codetitles_str); 3911: %{$cat_titles} = &str2hash($cat_titles_str); 3912: %{$cat_order} = &str2hash($cat_order_str); 3913: return 'ok'; 3914: } 3915: return $response; 3916: } 3917: 3918: # ------------------------------------------------------- Course Group routines 3919: 3920: sub get_coursegroups { 3921: my ($cdom,$cnum,$group) = @_; 3922: return(&dump('coursegroups',$cdom,$cnum,$group)); 3923: } 3924: 3925: sub modify_coursegroup { 3926: my ($cdom,$cnum,$groupsettings) = @_; 3927: return(&put('coursegroups',$groupsettings,$cdom,$cnum)); 3928: } 3929: 3930: sub modify_group_roles { 3931: my ($cdom,$cnum,$group_id,$user,$end,$start,$userprivs) = @_; 3932: my $url = '/'.$cdom.'/'.$cnum.'/'.$group_id; 3933: my $role = 'gr/'.&escape($userprivs); 3934: my ($uname,$udom) = split(/:/,$user); 3935: my $result = &assignrole($udom,$uname,$url,$role,$end,$start); 3936: if ($result eq 'ok') { 3937: &devalidate_getgroups_cache($udom,$uname,$cdom,$cnum); 3938: } 3939: 3940: return $result; 3941: } 3942: 3943: sub modify_coursegroup_membership { 3944: my ($cdom,$cnum,$membership) = @_; 3945: my $result = &put('groupmembership',$membership,$cdom,$cnum); 3946: return $result; 3947: } 3948: 3949: sub get_active_groups { 3950: my ($udom,$uname,$cdom,$cnum) = @_; 3951: my $now = time; 3952: my %groups = (); 3953: foreach my $key (keys(%env)) { 3954: if ($key =~ m-user\.role\.gr\./([^/]+)/([^/]+)/(\w+)$-) { 3955: my ($start,$end) = split(/\./,$env{$key}); 3956: if (($end!=0) && ($end<$now)) { next; } 3957: if (($start!=0) && ($start>$now)) { next; } 3958: if ($1 eq $cdom && $2 eq $cnum) { 3959: $groups{$3} = $env{$key} ; 3960: } 3961: } 3962: } 3963: return %groups; 3964: } 3965: 3966: sub get_group_membership { 3967: my ($cdom,$cnum,$group) = @_; 3968: return(&dump('groupmembership',$cdom,$cnum,$group)); 3969: } 3970: 3971: sub get_users_groups { 3972: my ($udom,$uname,$courseid) = @_; 3973: my $cachetime=1800; 3974: $courseid=~s/\_/\//g; 3975: $courseid=~s/^(\w)/\/$1/; 3976: 3977: my $hashid="$udom:$uname:$courseid"; 3978: my ($result,$cached)=&is_cached_new('getgroups',$hashid); 3979: if (defined($cached)) { return $result; } 3980: 3981: my %roleshash = &dump('roles',$udom,$uname,$courseid); 3982: my ($tmp) = keys(%roleshash); 3983: if ($tmp=~/^error:/) { 3984: &logthis('Error retrieving roles: '.$tmp.' for '.$uname.':'.$udom); 3985: return ''; 3986: } else { 3987: my $grouplist; 3988: foreach my $key (keys %roleshash) { 3989: if ($key =~ /^\Q$courseid\E\/(\w+)\_gr$/) { 3990: unless ($roleshash{$key} =~ /_1_1$/) { # deleted membership 3991: $grouplist .= $1.':'; 3992: } 3993: } 3994: } 3995: $grouplist =~ s/:$//; 3996: return &do_cache_new('getgroups',$hashid,$grouplist,$cachetime); 3997: } 3998: } 3999: 4000: sub devalidate_getgroups_cache { 4001: my ($udom,$uname,$cdom,$cnum)=@_; 4002: my $courseid = $cdom.'_'.$cnum; 4003: $courseid=~s/\_/\//g; 4004: $courseid=~s/^(\w)/\/$1/; 4005: my $hashid="$udom:$uname:$courseid"; 4006: &devalidate_cache_new('getgroups',$hashid); 4007: } 4008: 4009: # ------------------------------------------------------------------ Plain Text 4010: 4011: sub plaintext { 4012: my $short=shift; 4013: return &Apache::lonlocal::mt($prp{$short}); 4014: } 4015: 4016: # ----------------------------------------------------------------- Assign Role 4017: 4018: sub assignrole { 4019: my ($udom,$uname,$url,$role,$end,$start,$deleteflag)=@_; 4020: my $mrole; 4021: if ($role =~ /^cr\//) { 4022: my $cwosec=$url; 4023: $cwosec=~s/^\/(\w+)\/(\w+)\/.*/$1\/$2/; 4024: unless (&allowed('ccr',$cwosec)) { 4025: &logthis('Refused custom assignrole: '. 4026: $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '. 4027: $env{'user.name'}.' at '.$env{'user.domain'}); 4028: return 'refused'; 4029: } 4030: $mrole='cr'; 4031: } elsif ($role =~ /^gr\//) { 4032: my $cwogrp=$url; 4033: $cwogrp=~s/^\/(\w+)\/(\w+)\/.*/$1\/$2/; 4034: unless (&allowed('mdg',$cwogrp)) { 4035: &logthis('Refused group assignrole: '. 4036: $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '. 4037: $env{'user.name'}.' at '.$env{'user.domain'}); 4038: return 'refused'; 4039: } 4040: $mrole='gr'; 4041: } else { 4042: my $cwosec=$url; 4043: $cwosec=~s/^\/(\w+)\/(\w+)\/.*/$1\/$2/; 4044: unless ((&allowed('c'.$role,$cwosec)) || &allowed('c'.$role,$udom)) { 4045: &logthis('Refused assignrole: '. 4046: $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '. 4047: $env{'user.name'}.' at '.$env{'user.domain'}); 4048: return 'refused'; 4049: } 4050: $mrole=$role; 4051: } 4052: my $command="encrypt:rolesput:$env{'user.domain'}:$env{'user.name'}:". 4053: "$udom:$uname:$url".'_'."$mrole=$role"; 4054: if ($end) { $command.='_'.$end; } 4055: if ($start) { 4056: if ($end) { 4057: $command.='_'.$start; 4058: } else { 4059: $command.='_0_'.$start; 4060: } 4061: } 4062: # actually delete 4063: if ($deleteflag) { 4064: if ((&allowed('dro',$udom)) || (&allowed('dro',$url))) { 4065: # modify command to delete the role 4066: $command="encrypt:rolesdel:$env{'user.domain'}:$env{'user.name'}:". 4067: "$udom:$uname:$url".'_'."$mrole"; 4068: &logthis("$env{'user.name'} at $env{'user.domain'} deletes $mrole in $url for $uname at $udom"); 4069: # set start and finish to negative values for userrolelog 4070: $start=-1; 4071: $end=-1; 4072: } 4073: } 4074: # send command 4075: my $answer=&reply($command,&homeserver($uname,$udom)); 4076: # log new user role if status is ok 4077: if ($answer eq 'ok') { 4078: &userrolelog($role,$uname,$udom,$url,$start,$end); 4079: } 4080: return $answer; 4081: } 4082: 4083: # -------------------------------------------------- Modify user authentication 4084: # Overrides without validation 4085: 4086: sub modifyuserauth { 4087: my ($udom,$uname,$umode,$upass)=@_; 4088: my $uhome=&homeserver($uname,$udom); 4089: unless (&allowed('mau',$udom)) { return 'refused'; } 4090: &logthis('Call to modify user authentication '.$udom.', '.$uname.', '. 4091: $umode.' by '.$env{'user.name'}.' at '.$env{'user.domain'}. 4092: ' in domain '.$env{'request.role.domain'}); 4093: my $reply=&reply('encrypt:changeuserauth:'.$udom.':'.$uname.':'.$umode.':'. 4094: &escape($upass),$uhome); 4095: &log($env{'user.domain'},$env{'user.name'},$env{'user.home'}, 4096: 'Authentication changed for '.$udom.', '.$uname.', '.$umode. 4097: '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply); 4098: &log($udom,,$uname,$uhome, 4099: 'Authentication changed by '.$env{'user.domain'}.', '. 4100: $env{'user.name'}.', '.$umode. 4101: '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply); 4102: unless ($reply eq 'ok') { 4103: &logthis('Authentication mode error: '.$reply); 4104: return 'error: '.$reply; 4105: } 4106: return 'ok'; 4107: } 4108: 4109: # --------------------------------------------------------------- Modify a user 4110: 4111: sub modifyuser { 4112: my ($udom, $uname, $uid, 4113: $umode, $upass, $first, 4114: $middle, $last, $gene, 4115: $forceid, $desiredhome, $email)=@_; 4116: $udom=~s/\W//g; 4117: $uname=~s/\W//g; 4118: &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '. 4119: $umode.', '.$first.', '.$middle.', '. 4120: $last.', '.$gene.'(forceid: '.$forceid.')'. 4121: (defined($desiredhome) ? ' desiredhome = '.$desiredhome : 4122: ' desiredhome not specified'). 4123: ' by '.$env{'user.name'}.' at '.$env{'user.domain'}. 4124: ' in domain '.$env{'request.role.domain'}); 4125: my $uhome=&homeserver($uname,$udom,'true'); 4126: # ----------------------------------------------------------------- Create User 4127: if (($uhome eq 'no_host') && 4128: (($umode && $upass) || ($umode eq 'localauth'))) { 4129: my $unhome=''; 4130: if (defined($desiredhome) && $hostdom{$desiredhome} eq $udom) { 4131: $unhome = $desiredhome; 4132: } elsif($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom) { 4133: $unhome=$env{'course.'.$env{'request.course.id'}.'.home'}; 4134: } else { # load balancing routine for determining $unhome 4135: my $tryserver; 4136: my $loadm=10000000; 4137: foreach $tryserver (keys %libserv) { 4138: if ($hostdom{$tryserver} eq $udom) { 4139: my $answer=reply('load',$tryserver); 4140: if (($answer=~/\d+/) && ($answer<$loadm)) { 4141: $loadm=$answer; 4142: $unhome=$tryserver; 4143: } 4144: } 4145: } 4146: } 4147: if (($unhome eq '') || ($unhome eq 'no_host')) { 4148: return 'error: unable to find a home server for '.$uname. 4149: ' in domain '.$udom; 4150: } 4151: my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':'.$umode.':'. 4152: &escape($upass),$unhome); 4153: unless ($reply eq 'ok') { 4154: return 'error: '.$reply; 4155: } 4156: $uhome=&homeserver($uname,$udom,'true'); 4157: if (($uhome eq '') || ($uhome eq 'no_host') || ($uhome ne $unhome)) { 4158: return 'error: unable verify users home machine.'; 4159: } 4160: } # End of creation of new user 4161: # ---------------------------------------------------------------------- Add ID 4162: if ($uid) { 4163: $uid=~tr/A-Z/a-z/; 4164: my %uidhash=&idrget($udom,$uname); 4165: if (($uidhash{$uname}) && ($uidhash{$uname}!~/error\:/) 4166: && (!$forceid)) { 4167: unless ($uid eq $uidhash{$uname}) { 4168: return 'error: user id "'.$uid.'" does not match '. 4169: 'current user id "'.$uidhash{$uname}.'".'; 4170: } 4171: } else { 4172: &idput($udom,($uname => $uid)); 4173: } 4174: } 4175: # -------------------------------------------------------------- Add names, etc 4176: my @tmp=&get('environment', 4177: ['firstname','middlename','lastname','generation'], 4178: $udom,$uname); 4179: my %names; 4180: if ($tmp[0] =~ m/^error:.*/) { 4181: %names=(); 4182: } else { 4183: %names = @tmp; 4184: } 4185: # 4186: # Make sure to not trash student environment if instructor does not bother 4187: # to supply name and email information 4188: # 4189: if ($first) { $names{'firstname'} = $first; } 4190: if (defined($middle)) { $names{'middlename'} = $middle; } 4191: if ($last) { $names{'lastname'} = $last; } 4192: if (defined($gene)) { $names{'generation'} = $gene; } 4193: if ($email) { 4194: $email=~s/[^\w\@\.\-\,]//gs; 4195: if ($email=~/\@/) { $names{'notification'} = $email; 4196: $names{'critnotification'} = $email; 4197: $names{'permanentemail'} = $email; } 4198: } 4199: my $reply = &put('environment', \%names, $udom,$uname); 4200: if ($reply ne 'ok') { return 'error: '.$reply; } 4201: &devalidate_cache_new('namescache',$uname.':'.$udom); 4202: &logthis('Success modifying user '.$udom.', '.$uname.', '.$uid.', '. 4203: $umode.', '.$first.', '.$middle.', '. 4204: $last.', '.$gene.' by '. 4205: $env{'user.name'}.' at '.$env{'user.domain'}); 4206: return 'ok'; 4207: } 4208: 4209: # -------------------------------------------------------------- Modify student 4210: 4211: sub modifystudent { 4212: my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec, 4213: $end,$start,$forceid,$desiredhome,$email,$type,$locktype,$cid)=@_; 4214: if (!$cid) { 4215: unless ($cid=$env{'request.course.id'}) { 4216: return 'not_in_class'; 4217: } 4218: } 4219: # --------------------------------------------------------------- Make the user 4220: my $reply=&modifyuser 4221: ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$forceid, 4222: $desiredhome,$email); 4223: unless ($reply eq 'ok') { return $reply; } 4224: # This will cause &modify_student_enrollment to get the uid from the 4225: # students environment 4226: $uid = undef if (!$forceid); 4227: $reply = &modify_student_enrollment($udom,$uname,$uid,$first,$middle,$last, 4228: $gene,$usec,$end,$start,$type,$locktype,$cid); 4229: return $reply; 4230: } 4231: 4232: sub modify_student_enrollment { 4233: my ($udom,$uname,$uid,$first,$middle,$last,$gene,$usec,$end,$start,$type,$locktype,$cid) = @_; 4234: my ($cdom,$cnum,$chome); 4235: if (!$cid) { 4236: unless ($cid=$env{'request.course.id'}) { 4237: return 'not_in_class'; 4238: } 4239: $cdom=$env{'course.'.$cid.'.domain'}; 4240: $cnum=$env{'course.'.$cid.'.num'}; 4241: } else { 4242: ($cdom,$cnum)=split(/_/,$cid); 4243: } 4244: $chome=$env{'course.'.$cid.'.home'}; 4245: if (!$chome) { 4246: $chome=&homeserver($cnum,$cdom); 4247: } 4248: if (!$chome) { return 'unknown_course'; } 4249: # Make sure the user exists 4250: my $uhome=&homeserver($uname,$udom); 4251: if (($uhome eq '') || ($uhome eq 'no_host')) { 4252: return 'error: no such user'; 4253: } 4254: # Get student data if we were not given enough information 4255: if (!defined($first) || $first eq '' || 4256: !defined($last) || $last eq '' || 4257: !defined($uid) || $uid eq '' || 4258: !defined($middle) || $middle eq '' || 4259: !defined($gene) || $gene eq '') { 4260: # They did not supply us with enough data to enroll the student, so 4261: # we need to pick up more information. 4262: my %tmp = &get('environment', 4263: ['firstname','middlename','lastname', 'generation','id'] 4264: ,$udom,$uname); 4265: 4266: #foreach (keys(%tmp)) { 4267: # &logthis("key $_ = ".$tmp{$_}); 4268: #} 4269: $first = $tmp{'firstname'} if (!defined($first) || $first eq ''); 4270: $middle = $tmp{'middlename'} if (!defined($middle) || $middle eq ''); 4271: $last = $tmp{'lastname'} if (!defined($last) || $last eq ''); 4272: $gene = $tmp{'generation'} if (!defined($gene) || $gene eq ''); 4273: $uid = $tmp{'id'} if (!defined($uid) || $uid eq ''); 4274: } 4275: my $fullname = &format_name($first,$middle,$last,$gene,'lastname'); 4276: my $reply=cput('classlist', 4277: {"$uname:$udom" => 4278: join(':',$end,$start,$uid,$usec,$fullname,$type,$locktype) }, 4279: $cdom,$cnum); 4280: unless (($reply eq 'ok') || ($reply eq 'delayed')) { 4281: return 'error: '.$reply; 4282: } else { 4283: &devalidate_getsection_cache($udom,$uname,$cid); 4284: } 4285: # Add student role to user 4286: my $uurl='/'.$cid; 4287: $uurl=~s/\_/\//g; 4288: if ($usec) { 4289: $uurl.='/'.$usec; 4290: } 4291: return &assignrole($udom,$uname,$uurl,'st',$end,$start); 4292: } 4293: 4294: sub format_name { 4295: my ($firstname,$middlename,$lastname,$generation,$first)=@_; 4296: my $name; 4297: if ($first ne 'lastname') { 4298: $name=$firstname.' '.$middlename.' '.$lastname.' '.$generation; 4299: } else { 4300: if ($lastname=~/\S/) { 4301: $name.= $lastname.' '.$generation.', '.$firstname.' '.$middlename; 4302: $name=~s/\s+,/,/; 4303: } else { 4304: $name.= $firstname.' '.$middlename.' '.$generation; 4305: } 4306: } 4307: $name=~s/^\s+//; 4308: $name=~s/\s+$//; 4309: $name=~s/\s+/ /g; 4310: return $name; 4311: } 4312: 4313: # ------------------------------------------------- Write to course preferences 4314: 4315: sub writecoursepref { 4316: my ($courseid,%prefs)=@_; 4317: $courseid=~s/^\///; 4318: $courseid=~s/\_/\//g; 4319: my ($cdomain,$cnum)=split(/\//,$courseid); 4320: my $chome=homeserver($cnum,$cdomain); 4321: if (($chome eq '') || ($chome eq 'no_host')) { 4322: return 'error: no such course'; 4323: } 4324: my $cstring=''; 4325: foreach (keys %prefs) { 4326: $cstring.=escape($_).'='.escape($prefs{$_}).'&'; 4327: } 4328: $cstring=~s/\&$//; 4329: return reply('put:'.$cdomain.':'.$cnum.':environment:'.$cstring,$chome); 4330: } 4331: 4332: # ---------------------------------------------------------- Make/modify course 4333: 4334: sub createcourse { 4335: my ($udom,$description,$url,$course_server,$nonstandard,$inst_code,$course_owner)=@_; 4336: $url=&declutter($url); 4337: my $cid=''; 4338: unless (&allowed('ccc',$udom)) { 4339: return 'refused'; 4340: } 4341: # ------------------------------------------------------------------- Create ID 4342: my $uname=int(1+rand(9)). 4343: ('a'..'z','A'..'Z','0'..'9')[int(rand(62))]. 4344: substr($$.time,0,5).unpack("H8",pack("I32",time)). 4345: unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'}; 4346: # ----------------------------------------------- Make sure that does not exist 4347: my $uhome=&homeserver($uname,$udom,'true'); 4348: unless (($uhome eq '') || ($uhome eq 'no_host')) { 4349: $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)). 4350: unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'}; 4351: $uhome=&homeserver($uname,$udom,'true'); 4352: unless (($uhome eq '') || ($uhome eq 'no_host')) { 4353: return 'error: unable to generate unique course-ID'; 4354: } 4355: } 4356: # ------------------------------------------------ Check supplied server name 4357: $course_server = $env{'user.homeserver'} if (! defined($course_server)); 4358: if (! exists($libserv{$course_server})) { 4359: return 'error:bad server name '.$course_server; 4360: } 4361: # ------------------------------------------------------------- Make the course 4362: my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::', 4363: $course_server); 4364: unless ($reply eq 'ok') { return 'error: '.$reply; } 4365: $uhome=&homeserver($uname,$udom,'true'); 4366: if (($uhome eq '') || ($uhome eq 'no_host')) { 4367: return 'error: no such course'; 4368: } 4369: # ----------------------------------------------------------------- Course made 4370: # log existence 4371: &courseidput($udom,&escape($udom.'_'.$uname).'='.&escape($description). 4372: ':'.&escape($inst_code).':'.&escape($course_owner),$uhome); 4373: &flushcourselogs(); 4374: # set toplevel url 4375: my $topurl=$url; 4376: unless ($nonstandard) { 4377: # ------------------------------------------ For standard courses, make top url 4378: my $mapurl=&clutter($url); 4379: if ($mapurl eq '/res/') { $mapurl=''; } 4380: $env{'form.initmap'}=(<<ENDINITMAP); 4381: <map> 4382: <resource id="1" type="start"></resource> 4383: <resource id="2" src="$mapurl"></resource> 4384: <resource id="3" type="finish"></resource> 4385: <link index="1" from="1" to="2"></link> 4386: <link index="2" from="2" to="3"></link> 4387: </map> 4388: ENDINITMAP 4389: $topurl=&declutter( 4390: &finishuserfileupload($uname,$udom,'initmap','default.sequence') 4391: ); 4392: } 4393: # ----------------------------------------------------------- Write preferences 4394: &writecoursepref($udom.'_'.$uname, 4395: ('description' => $description, 4396: 'url' => $topurl)); 4397: return '/'.$udom.'/'.$uname; 4398: } 4399: 4400: # ---------------------------------------------------------- Assign Custom Role 4401: 4402: sub assigncustomrole { 4403: my ($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start,$deleteflag)=@_; 4404: return &assignrole($udom,$uname,$url,'cr/'.$rdom.'/'.$rnam.'/'.$rolename, 4405: $end,$start,$deleteflag); 4406: } 4407: 4408: # ----------------------------------------------------------------- Revoke Role 4409: 4410: sub revokerole { 4411: my ($udom,$uname,$url,$role,$deleteflag)=@_; 4412: my $now=time; 4413: return &assignrole($udom,$uname,$url,$role,$now,$deleteflag); 4414: } 4415: 4416: # ---------------------------------------------------------- Revoke Custom Role 4417: 4418: sub revokecustomrole { 4419: my ($udom,$uname,$url,$rdom,$rnam,$rolename,$deleteflag)=@_; 4420: my $now=time; 4421: return &assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$now, 4422: $deleteflag); 4423: } 4424: 4425: # ------------------------------------------------------------ Disk usage 4426: sub diskusage { 4427: my ($udom,$uname,$directoryRoot)=@_; 4428: $directoryRoot =~ s/\/$//; 4429: my $listing=&reply('du:'.$directoryRoot,homeserver($uname,$udom)); 4430: return $listing; 4431: } 4432: 4433: sub is_locked { 4434: my ($file_name, $domain, $user) = @_; 4435: my @check; 4436: my $is_locked; 4437: push @check, $file_name; 4438: my %locked = &get('file_permissions',\@check, 4439: $env{'user.domain'},$env{'user.name'}); 4440: my ($tmp)=keys(%locked); 4441: if ($tmp=~/^error:/) { undef(%locked); } 4442: 4443: if (ref($locked{$file_name}) eq 'ARRAY') { 4444: $is_locked = 'true'; 4445: } else { 4446: $is_locked = 'false'; 4447: } 4448: } 4449: 4450: # ------------------------------------------------------------- Mark as Read Only 4451: 4452: sub mark_as_readonly { 4453: my ($domain,$user,$files,$what) = @_; 4454: my %current_permissions = &dump('file_permissions',$domain,$user); 4455: my ($tmp)=keys(%current_permissions); 4456: if ($tmp=~/^error:/) { undef(%current_permissions); } 4457: foreach my $file (@{$files}) { 4458: push(@{$current_permissions{$file}},$what); 4459: } 4460: &put('file_permissions',\%current_permissions,$domain,$user); 4461: return; 4462: } 4463: 4464: # ------------------------------------------------------------Save Selected Files 4465: 4466: sub save_selected_files { 4467: my ($user, $path, @files) = @_; 4468: my $filename = $user."savedfiles"; 4469: my @other_files = &files_not_in_path($user, $path); 4470: open (OUT, '>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename); 4471: foreach my $file (@files) { 4472: print (OUT $env{'form.currentpath'}.$file."\n"); 4473: } 4474: foreach my $file (@other_files) { 4475: print (OUT $file."\n"); 4476: } 4477: close (OUT); 4478: return 'ok'; 4479: } 4480: 4481: sub clear_selected_files { 4482: my ($user) = @_; 4483: my $filename = $user."savedfiles"; 4484: open (OUT, '>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename); 4485: print (OUT undef); 4486: close (OUT); 4487: return ("ok"); 4488: } 4489: 4490: sub files_in_path { 4491: my ($user, $path) = @_; 4492: my $filename = $user."savedfiles"; 4493: my %return_files; 4494: open (IN, '<'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename); 4495: while (my $line_in = <IN>) { 4496: chomp ($line_in); 4497: my @paths_and_file = split (m!/!, $line_in); 4498: my $file_part = pop (@paths_and_file); 4499: my $path_part = join ('/', @paths_and_file); 4500: $path_part.='/'; 4501: my $path_and_file = $path_part.$file_part; 4502: if ($path_part eq $path) { 4503: $return_files{$file_part}= 'selected'; 4504: } 4505: } 4506: close (IN); 4507: return (\%return_files); 4508: } 4509: 4510: # called in portfolio select mode, to show files selected NOT in current directory 4511: sub files_not_in_path { 4512: my ($user, $path) = @_; 4513: my $filename = $user."savedfiles"; 4514: my @return_files; 4515: my $path_part; 4516: open (IN, '<'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename); 4517: while (<IN>) { 4518: #ok, I know it's clunky, but I want it to work 4519: my @paths_and_file = split m!/!, $_; 4520: my $file_part = pop (@paths_and_file); 4521: chomp ($file_part); 4522: my $path_part = join ('/', @paths_and_file); 4523: $path_part .= '/'; 4524: my $path_and_file = $path_part.$file_part; 4525: if ($path_part ne $path) { 4526: push (@return_files, ($path_and_file)); 4527: } 4528: } 4529: close (OUT); 4530: return (@return_files); 4531: } 4532: 4533: #--------------------------------------------------------------Get Marked as Read Only 4534: 4535: 4536: sub get_marked_as_readonly { 4537: my ($domain,$user,$what) = @_; 4538: my %current_permissions = &dump('file_permissions',$domain,$user); 4539: my ($tmp)=keys(%current_permissions); 4540: if ($tmp=~/^error:/) { undef(%current_permissions); } 4541: my @readonly_files; 4542: my $cmp1=$what; 4543: if (ref($what)) { $cmp1=join('',@{$what}) }; 4544: while (my ($file_name,$value) = each(%current_permissions)) { 4545: if (ref($value) eq "ARRAY"){ 4546: foreach my $stored_what (@{$value}) { 4547: my $cmp2=$stored_what; 4548: if (ref($stored_what)) { $cmp2=join('',@{$stored_what}) }; 4549: if ($cmp1 eq $cmp2) { 4550: push(@readonly_files, $file_name); 4551: } elsif (!defined($what)) { 4552: push(@readonly_files, $file_name); 4553: } 4554: } 4555: } 4556: } 4557: return @readonly_files; 4558: } 4559: #-----------------------------------------------------------Get Marked as Read Only Hash 4560: 4561: sub get_marked_as_readonly_hash { 4562: my ($domain,$user,$what) = @_; 4563: my %current_permissions = &dump('file_permissions',$domain,$user); 4564: my ($tmp)=keys(%current_permissions); 4565: if ($tmp=~/^error:/) { undef(%current_permissions); } 4566: 4567: my %readonly_files; 4568: while (my ($file_name,$value) = each(%current_permissions)) { 4569: if (ref($value) eq "ARRAY"){ 4570: foreach my $stored_what (@{$value}) { 4571: if ($stored_what eq $what) { 4572: $readonly_files{$file_name} = 'locked'; 4573: } elsif (!defined($what)) { 4574: $readonly_files{$file_name} = 'locked'; 4575: } 4576: } 4577: } 4578: } 4579: return %readonly_files; 4580: } 4581: # ------------------------------------------------------------ Unmark as Read Only 4582: 4583: sub unmark_as_readonly { 4584: # unmarks $file_name (if $file_name is defined), or all files locked by $what 4585: # for portfolio submissions, $what contains [$symb,$crsid] 4586: my ($domain,$user,$what,$file_name) = @_; 4587: my $symb_crs = $what; 4588: if (ref($what)) { $symb_crs=join('',@$what); } 4589: my %current_permissions = &dump('file_permissions',$domain,$user); 4590: my ($tmp)=keys(%current_permissions); 4591: if ($tmp=~/^error:/) { undef(%current_permissions); } 4592: my @readonly_files = &get_marked_as_readonly($domain,$user,$what); 4593: foreach my $file (@readonly_files) { 4594: if (defined($file_name) && ($file_name ne $file)) { next; } 4595: my $current_locks = $current_permissions{$file}; 4596: my @new_locks; 4597: my @del_keys; 4598: if (ref($current_locks) eq "ARRAY"){ 4599: foreach my $locker (@{$current_locks}) { 4600: my $compare=$locker; 4601: if (ref($locker)) { $compare=join('',@{$locker}) }; 4602: if ($compare ne $symb_crs) { 4603: push(@new_locks, $locker); 4604: } 4605: } 4606: if (scalar(@new_locks) > 0) { 4607: $current_permissions{$file} = \@new_locks; 4608: } else { 4609: push(@del_keys, $file); 4610: &del('file_permissions',\@del_keys, $domain, $user); 4611: delete($current_permissions{$file}); 4612: } 4613: } 4614: } 4615: &put('file_permissions',\%current_permissions,$domain,$user); 4616: return; 4617: } 4618: 4619: # ------------------------------------------------------------ Directory lister 4620: 4621: sub dirlist { 4622: my ($uri,$userdomain,$username,$alternateDirectoryRoot)=@_; 4623: 4624: $uri=~s/^\///; 4625: $uri=~s/\/$//; 4626: my ($udom, $uname); 4627: (undef,$udom,$uname)=split(/\//,$uri); 4628: if(defined($userdomain)) { 4629: $udom = $userdomain; 4630: } 4631: if(defined($username)) { 4632: $uname = $username; 4633: } 4634: 4635: my $dirRoot = $perlvar{'lonDocRoot'}; 4636: if(defined($alternateDirectoryRoot)) { 4637: $dirRoot = $alternateDirectoryRoot; 4638: $dirRoot =~ s/\/$//; 4639: } 4640: 4641: if($udom) { 4642: if($uname) { 4643: my $listing=reply('ls2:'.$dirRoot.'/'.$uri, 4644: homeserver($uname,$udom)); 4645: my @listing_results; 4646: if ($listing eq 'unknown_cmd') { 4647: $listing=reply('ls:'.$dirRoot.'/'.$uri, 4648: homeserver($uname,$udom)); 4649: @listing_results = split(/:/,$listing); 4650: } else { 4651: @listing_results = map { &unescape($_); } split(/:/,$listing); 4652: } 4653: return @listing_results; 4654: } elsif(!defined($alternateDirectoryRoot)) { 4655: my $tryserver; 4656: my %allusers=(); 4657: foreach $tryserver (keys %libserv) { 4658: if($hostdom{$tryserver} eq $udom) { 4659: my $listing=reply('ls2:'.$perlvar{'lonDocRoot'}.'/res/'. 4660: $udom, $tryserver); 4661: my @listing_results; 4662: if ($listing eq 'unknown_cmd') { 4663: $listing=reply('ls:'.$perlvar{'lonDocRoot'}.'/res/'. 4664: $udom, $tryserver); 4665: @listing_results = split(/:/,$listing); 4666: } else { 4667: @listing_results = 4668: map { &unescape($_); } split(/:/,$listing); 4669: } 4670: if ($listing_results[0] ne 'no_such_dir' && 4671: $listing_results[0] ne 'empty' && 4672: $listing_results[0] ne 'con_lost') { 4673: foreach (@listing_results) { 4674: my ($entry,@stat)=split(/&/,$_); 4675: $allusers{$entry}=1; 4676: } 4677: } 4678: } 4679: } 4680: my $alluserstr=''; 4681: foreach (sort keys %allusers) { 4682: $alluserstr.=$_.'&user:'; 4683: } 4684: $alluserstr=~s/:$//; 4685: return split(/:/,$alluserstr); 4686: } else { 4687: my @emptyResults = (); 4688: push(@emptyResults, 'missing user name'); 4689: return split(':',@emptyResults); 4690: } 4691: } elsif(!defined($alternateDirectoryRoot)) { 4692: my $tryserver; 4693: my %alldom=(); 4694: foreach $tryserver (keys %libserv) { 4695: $alldom{$hostdom{$tryserver}}=1; 4696: } 4697: my $alldomstr=''; 4698: foreach (sort keys %alldom) { 4699: $alldomstr.=$perlvar{'lonDocRoot'}.'/res/'.$_.'/&domain:'; 4700: } 4701: $alldomstr=~s/:$//; 4702: return split(/:/,$alldomstr); 4703: } else { 4704: my @emptyResults = (); 4705: push(@emptyResults, 'missing domain'); 4706: return split(':',@emptyResults); 4707: } 4708: } 4709: 4710: # --------------------------------------------- GetFileTimestamp 4711: # This function utilizes dirlist and returns the date stamp for 4712: # when it was last modified. It will also return an error of -1 4713: # if an error occurs 4714: 4715: ## 4716: ## FIXME: This subroutine assumes its caller knows something about the 4717: ## directory structure of the home server for the student ($root). 4718: ## Not a good assumption to make. Since this is for looking up files 4719: ## in user directories, the full path should be constructed by lond, not 4720: ## whatever machine we request data from. 4721: ## 4722: sub GetFileTimestamp { 4723: my ($studentDomain,$studentName,$filename,$root)=@_; 4724: $studentDomain=~s/\W//g; 4725: $studentName=~s/\W//g; 4726: my $subdir=$studentName.'__'; 4727: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/; 4728: my $proname="$studentDomain/$subdir/$studentName"; 4729: $proname .= '/'.$filename; 4730: my ($fileStat) = &Apache::lonnet::dirlist($proname, $studentDomain, 4731: $studentName, $root); 4732: my @stats = split('&', $fileStat); 4733: if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') { 4734: # @stats contains first the filename, then the stat output 4735: return $stats[10]; # so this is 10 instead of 9. 4736: } else { 4737: return -1; 4738: } 4739: } 4740: 4741: sub stat_file { 4742: my ($uri) = @_; 4743: $uri = &clutter($uri); 4744: 4745: # we want just the url part without the unneeded accessor url bits 4746: if ($file =~ m-^/adm/-) { 4747: $file=~s-^/adm/wrapper/-/-; 4748: $file=~s-^/adm/coursedocs/showdoc/-/-; 4749: } 4750: 4751: my ($udom,$uname,$file,$dir); 4752: if ($uri =~ m-^/(uploaded|editupload)/-) { 4753: ($udom,$uname,$file) = 4754: ($uri =~ m-/(?:uploaded|editupload)/?([^/]*)/?([^/]*)/?(.*)-); 4755: $file = 'userfiles/'.$file; 4756: $dir = &Apache::loncommon::propath($udom,$uname); 4757: } 4758: if ($uri =~ m-^/res/-) { 4759: ($udom,$uname) = 4760: ($uri =~ m-/(?:res)/?([^/]*)/?([^/]*)/-); 4761: $file = $uri; 4762: } 4763: 4764: if (!$udom || !$uname || !$file) { 4765: # unable to handle the uri 4766: return (); 4767: } 4768: 4769: my ($result) = &dirlist($file,$udom,$uname,$dir); 4770: my @stats = split('&', $result); 4771: 4772: if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') { 4773: shift(@stats); #filename is first 4774: return @stats; 4775: } 4776: return (); 4777: } 4778: 4779: # -------------------------------------------------------- Value of a Condition 4780: 4781: # gets the value of a specific preevaluated condition 4782: # stored in the string $env{user.state.<cid>} 4783: # or looks up a condition reference in the bighash and if if hasn't 4784: # already been evaluated recurses into docondval to get the value of 4785: # the condition, then memoizing it to 4786: # $env{user.state.<cid>.<condition>} 4787: sub directcondval { 4788: my $number=shift; 4789: if (!defined($env{'user.state.'.$env{'request.course.id'}})) { 4790: &Apache::lonuserstate::evalstate(); 4791: } 4792: if (exists($env{'user.state.'.$env{'request.course.id'}.".$number"})) { 4793: return $env{'user.state.'.$env{'request.course.id'}.".$number"}; 4794: } elsif ($number =~ /^_/) { 4795: my $sub_condition; 4796: if (tie(my %bighash,'GDBM_File',$env{'request.course.fn'}.'.db', 4797: &GDBM_READER(),0640)) { 4798: $sub_condition=$bighash{'conditions'.$number}; 4799: untie(%bighash); 4800: } 4801: my $value = &docondval($sub_condition); 4802: &appenv('user.state.'.$env{'request.course.id'}.".$number" => $value); 4803: return $value; 4804: } 4805: if ($env{'user.state.'.$env{'request.course.id'}}) { 4806: return substr($env{'user.state.'.$env{'request.course.id'}},$number,1); 4807: } else { 4808: return 2; 4809: } 4810: } 4811: 4812: # get the collection of conditions for this resource 4813: sub condval { 4814: my $condidx=shift; 4815: my $allpathcond=''; 4816: foreach my $cond (split(/\|/,$condidx)) { 4817: if (defined($env{'acc.cond.'.$env{'request.course.id'}.'.'.$cond})) { 4818: $allpathcond.= 4819: '('.$env{'acc.cond.'.$env{'request.course.id'}.'.'.$cond}.')|'; 4820: } 4821: } 4822: $allpathcond=~s/\|$//; 4823: return &docondval($allpathcond); 4824: } 4825: 4826: #evaluates an expression of conditions 4827: sub docondval { 4828: my ($allpathcond) = @_; 4829: my $result=0; 4830: if ($env{'request.course.id'} 4831: && defined($allpathcond)) { 4832: my $operand='|'; 4833: my @stack; 4834: foreach my $chunk ($allpathcond=~/(\d+|_\d+\.\d+|\(|\)|\&|\|)/g) { 4835: if ($chunk eq '(') { 4836: push @stack,($operand,$result); 4837: } elsif ($chunk eq ')') { 4838: my $before=pop @stack; 4839: if (pop @stack eq '&') { 4840: $result=$result>$before?$before:$result; 4841: } else { 4842: $result=$result>$before?$result:$before; 4843: } 4844: } elsif (($chunk eq '&') || ($chunk eq '|')) { 4845: $operand=$chunk; 4846: } else { 4847: my $new=directcondval($chunk); 4848: if ($operand eq '&') { 4849: $result=$result>$new?$new:$result; 4850: } else { 4851: $result=$result>$new?$result:$new; 4852: } 4853: } 4854: } 4855: } 4856: return $result; 4857: } 4858: 4859: # ---------------------------------------------------- Devalidate courseresdata 4860: 4861: sub devalidatecourseresdata { 4862: my ($coursenum,$coursedomain)=@_; 4863: my $hashid=$coursenum.':'.$coursedomain; 4864: &devalidate_cache_new('courseres',$hashid); 4865: } 4866: 4867: # --------------------------------------------------- Course Resourcedata Query 4868: 4869: sub get_courseresdata { 4870: my ($coursenum,$coursedomain)=@_; 4871: my $coursehom=&homeserver($coursenum,$coursedomain); 4872: my $hashid=$coursenum.':'.$coursedomain; 4873: my ($result,$cached)=&is_cached_new('courseres',$hashid); 4874: my %dumpreply; 4875: unless (defined($cached)) { 4876: %dumpreply=&dump('resourcedata',$coursedomain,$coursenum); 4877: $result=\%dumpreply; 4878: my ($tmp) = keys(%dumpreply); 4879: if ($tmp !~ /^(con_lost|error|no_such_host)/i) { 4880: &do_cache_new('courseres',$hashid,$result,600); 4881: } elsif ($tmp =~ /^(con_lost|no_such_host)/) { 4882: return $tmp; 4883: } elsif ($tmp =~ /^(error)/) { 4884: $result=undef; 4885: &do_cache_new('courseres',$hashid,$result,600); 4886: } 4887: } 4888: return $result; 4889: } 4890: 4891: sub devalidateuserresdata { 4892: my ($uname,$udom)=@_; 4893: my $hashid="$udom:$uname"; 4894: &devalidate_cache_new('userres',$hashid); 4895: } 4896: 4897: sub get_userresdata { 4898: my ($uname,$udom)=@_; 4899: #most student don\'t have any data set, check if there is some data 4900: if (&EXT_cache_status($udom,$uname)) { return undef; } 4901: 4902: my $hashid="$udom:$uname"; 4903: my ($result,$cached)=&is_cached_new('userres',$hashid); 4904: if (!defined($cached)) { 4905: my %resourcedata=&dump('resourcedata',$udom,$uname); 4906: $result=\%resourcedata; 4907: &do_cache_new('userres',$hashid,$result,600); 4908: } 4909: my ($tmp)=keys(%$result); 4910: if (($tmp!~/^error\:/) && ($tmp!~/^con_lost/)) { 4911: return $result; 4912: } 4913: #error 2 occurs when the .db doesn't exist 4914: if ($tmp!~/error: 2 /) { 4915: &logthis("<font color=\"blue\">WARNING:". 4916: " Trying to get resource data for ". 4917: $uname." at ".$udom.": ". 4918: $tmp."</font>"); 4919: } elsif ($tmp=~/error: 2 /) { 4920: #&EXT_cache_set($udom,$uname); 4921: &do_cache_new('userres',$hashid,undef,600); 4922: undef($tmp); # not really an error so don't send it back 4923: } 4924: return $tmp; 4925: } 4926: 4927: sub resdata { 4928: my ($name,$domain,$type,@which)=@_; 4929: my $result; 4930: if ($type eq 'course') { 4931: $result=&get_courseresdata($name,$domain); 4932: } elsif ($type eq 'user') { 4933: $result=&get_userresdata($name,$domain); 4934: } 4935: if (!ref($result)) { return $result; } 4936: foreach my $item (@which) { 4937: if (defined($result->{$item})) { 4938: return $result->{$item}; 4939: } 4940: } 4941: return undef; 4942: } 4943: 4944: # 4945: # EXT resource caching routines 4946: # 4947: 4948: sub clear_EXT_cache_status { 4949: &delenv('cache.EXT.'); 4950: } 4951: 4952: sub EXT_cache_status { 4953: my ($target_domain,$target_user) = @_; 4954: my $cachename = 'cache.EXT.'.$target_user.'.'.$target_domain; 4955: if (exists($env{$cachename}) && ($env{$cachename}+600) > time) { 4956: # We know already the user has no data 4957: return 1; 4958: } else { 4959: return 0; 4960: } 4961: } 4962: 4963: sub EXT_cache_set { 4964: my ($target_domain,$target_user) = @_; 4965: my $cachename = 'cache.EXT.'.$target_user.'.'.$target_domain; 4966: #&appenv($cachename => time); 4967: } 4968: 4969: # --------------------------------------------------------- Value of a Variable 4970: sub EXT { 4971: 4972: my ($varname,$symbparm,$udom,$uname,$usection,$recurse)=@_; 4973: unless ($varname) { return ''; } 4974: #get real user name/domain, courseid and symb 4975: my $courseid; 4976: my $publicuser; 4977: if ($symbparm) { 4978: $symbparm=&get_symb_from_alias($symbparm); 4979: } 4980: if (!($uname && $udom)) { 4981: (my $cursymb,$courseid,$udom,$uname,$publicuser)= 4982: &Apache::lonxml::whichuser($symbparm); 4983: if (!$symbparm) { $symbparm=$cursymb; } 4984: } else { 4985: $courseid=$env{'request.course.id'}; 4986: } 4987: my ($realm,$space,$qualifier,@therest)=split(/\./,$varname); 4988: my $rest; 4989: if (defined($therest[0])) { 4990: $rest=join('.',@therest); 4991: } else { 4992: $rest=''; 4993: } 4994: 4995: my $qualifierrest=$qualifier; 4996: if ($rest) { $qualifierrest.='.'.$rest; } 4997: my $spacequalifierrest=$space; 4998: if ($qualifierrest) { $spacequalifierrest.='.'.$qualifierrest; } 4999: if ($realm eq 'user') { 5000: # --------------------------------------------------------------- user.resource 5001: if ($space eq 'resource') { 5002: if ( (defined($Apache::lonhomework::parsing_a_problem) 5003: || defined($Apache::lonhomework::parsing_a_task)) 5004: && 5005: ($symbparm eq &symbread()) ) { 5006: return $Apache::lonhomework::history{$qualifierrest}; 5007: } else { 5008: my %restored; 5009: if ($publicuser || $env{'request.state'} eq 'construct') { 5010: %restored=&tmprestore($symbparm,$courseid,$udom,$uname); 5011: } else { 5012: %restored=&restore($symbparm,$courseid,$udom,$uname); 5013: } 5014: return $restored{$qualifierrest}; 5015: } 5016: # ----------------------------------------------------------------- user.access 5017: } elsif ($space eq 'access') { 5018: # FIXME - not supporting calls for a specific user 5019: return &allowed($qualifier,$rest); 5020: # ------------------------------------------ user.preferences, user.environment 5021: } elsif (($space eq 'preferences') || ($space eq 'environment')) { 5022: if (($uname eq $env{'user.name'}) && 5023: ($udom eq $env{'user.domain'})) { 5024: return $env{join('.',('environment',$qualifierrest))}; 5025: } else { 5026: my %returnhash; 5027: if (!$publicuser) { 5028: %returnhash=&userenvironment($udom,$uname, 5029: $qualifierrest); 5030: } 5031: return $returnhash{$qualifierrest}; 5032: } 5033: # ----------------------------------------------------------------- user.course 5034: } elsif ($space eq 'course') { 5035: # FIXME - not supporting calls for a specific user 5036: return $env{join('.',('request.course',$qualifier))}; 5037: # ------------------------------------------------------------------- user.role 5038: } elsif ($space eq 'role') { 5039: # FIXME - not supporting calls for a specific user 5040: my ($role,$where)=split(/\./,$env{'request.role'}); 5041: if ($qualifier eq 'value') { 5042: return $role; 5043: } elsif ($qualifier eq 'extent') { 5044: return $where; 5045: } 5046: # ----------------------------------------------------------------- user.domain 5047: } elsif ($space eq 'domain') { 5048: return $udom; 5049: # ------------------------------------------------------------------- user.name 5050: } elsif ($space eq 'name') { 5051: return $uname; 5052: # ---------------------------------------------------- Any other user namespace 5053: } else { 5054: my %reply; 5055: if (!$publicuser) { 5056: %reply=&get($space,[$qualifierrest],$udom,$uname); 5057: } 5058: return $reply{$qualifierrest}; 5059: } 5060: } elsif ($realm eq 'query') { 5061: # ---------------------------------------------- pull stuff out of query string 5062: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 5063: [$spacequalifierrest]); 5064: return $env{'form.'.$spacequalifierrest}; 5065: } elsif ($realm eq 'request') { 5066: # ------------------------------------------------------------- request.browser 5067: if ($space eq 'browser') { 5068: if ($qualifier eq 'textremote') { 5069: if (&Apache::lonlocal::mt('textual_remote_display') eq 'on') { 5070: return 1; 5071: } else { 5072: return 0; 5073: } 5074: } else { 5075: return $env{'browser.'.$qualifier}; 5076: } 5077: # ------------------------------------------------------------ request.filename 5078: } else { 5079: return $env{'request.'.$spacequalifierrest}; 5080: } 5081: } elsif ($realm eq 'course') { 5082: # ---------------------------------------------------------- course.description 5083: return $env{'course.'.$courseid.'.'.$spacequalifierrest}; 5084: } elsif ($realm eq 'resource') { 5085: 5086: if (defined($courseid) && $courseid eq $env{'request.course.id'}) { 5087: if (!$symbparm) { $symbparm=&symbread(); } 5088: } 5089: 5090: if ($space eq 'title') { 5091: if (!$symbparm) { $symbparm = $env{'request.filename'}; } 5092: return &gettitle($symbparm); 5093: } 5094: 5095: if ($space eq 'map') { 5096: my ($map) = &decode_symb($symbparm); 5097: return &symbread($map); 5098: } 5099: 5100: my ($section, $group, @groups); 5101: my ($courselevelm,$courselevel); 5102: if ($symbparm && defined($courseid) && 5103: $courseid eq $env{'request.course.id'}) { 5104: 5105: #print '<br>'.$space.' - '.$qualifier.' - '.$spacequalifierrest; 5106: 5107: # ----------------------------------------------------- Cascading lookup scheme 5108: my $symbp=$symbparm; 5109: my $mapp=(&decode_symb($symbp))[0]; 5110: 5111: my $symbparm=$symbp.'.'.$spacequalifierrest; 5112: my $mapparm=$mapp.'___(all).'.$spacequalifierrest; 5113: 5114: if (($env{'user.name'} eq $uname) && 5115: ($env{'user.domain'} eq $udom)) { 5116: $section=$env{'request.course.sec'}; 5117: @groups=&sort_course_groups($env{'request.course.groups'},$courseid); 5118: if (@groups > 0) { 5119: @groups = sort(@groups); 5120: } 5121: } else { 5122: if (! defined($usection)) { 5123: $section=&getsection($udom,$uname,$courseid); 5124: } else { 5125: $section = $usection; 5126: } 5127: my $grouplist = &get_users_groups($udom,$uname,$courseid); 5128: if ($grouplist) { 5129: @groups=&sort_course_groups($grouplist,$courseid); 5130: } 5131: } 5132: 5133: my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest; 5134: my $seclevelr=$courseid.'.['.$section.'].'.$symbparm; 5135: my $seclevelm=$courseid.'.['.$section.'].'.$mapparm; 5136: 5137: $courselevel=$courseid.'.'.$spacequalifierrest; 5138: my $courselevelr=$courseid.'.'.$symbparm; 5139: $courselevelm=$courseid.'.'.$mapparm; 5140: 5141: # ----------------------------------------------------------- first, check user 5142: 5143: my $userreply=&resdata($uname,$udom,'user', 5144: ($courselevelr,$courselevelm, 5145: $courselevel)); 5146: if (defined($userreply)) { return $userreply; } 5147: 5148: # ------------------------------------------------ second, check some of course 5149: my $coursereply; 5150: if (@groups > 0) { 5151: $coursereply = &check_group_parms($courseid,\@groups,$symbparm, 5152: $mapparm,$spacequalifierrest); 5153: if (defined($coursereply)) { return $coursereply; } 5154: } 5155: 5156: $coursereply=&resdata($env{'course.'.$courseid.'.num'}, 5157: $env{'course.'.$courseid.'.domain'}, 5158: 'course', 5159: ($seclevelr,$seclevelm,$seclevel, 5160: $courselevelr)); 5161: if (defined($coursereply)) { return $coursereply; } 5162: 5163: # ------------------------------------------------------ third, check map parms 5164: my %parmhash=(); 5165: my $thisparm=''; 5166: if (tie(%parmhash,'GDBM_File', 5167: $env{'request.course.fn'}.'_parms.db', 5168: &GDBM_READER(),0640)) { 5169: $thisparm=$parmhash{$symbparm}; 5170: untie(%parmhash); 5171: } 5172: if ($thisparm) { return $thisparm; } 5173: } 5174: # ------------------------------------------ fourth, look in resource metadata 5175: 5176: $spacequalifierrest=~s/\./\_/; 5177: my $filename; 5178: if (!$symbparm) { $symbparm=&symbread(); } 5179: if ($symbparm) { 5180: $filename=(&decode_symb($symbparm))[2]; 5181: } else { 5182: $filename=$env{'request.filename'}; 5183: } 5184: my $metadata=&metadata($filename,$spacequalifierrest); 5185: if (defined($metadata)) { return $metadata; } 5186: $metadata=&metadata($filename,'parameter_'.$spacequalifierrest); 5187: if (defined($metadata)) { return $metadata; } 5188: 5189: # ---------------------------------------------- fourth, look in rest pf course 5190: if ($symbparm && defined($courseid) && 5191: $courseid eq $env{'request.course.id'}) { 5192: my $coursereply=&resdata($env{'course.'.$courseid.'.num'}, 5193: $env{'course.'.$courseid.'.domain'}, 5194: 'course', 5195: ($courselevelm,$courselevel)); 5196: if (defined($coursereply)) { return $coursereply; } 5197: } 5198: # ------------------------------------------------------------------ Cascade up 5199: unless ($space eq '0') { 5200: my @parts=split(/_/,$space); 5201: my $id=pop(@parts); 5202: my $part=join('_',@parts); 5203: if ($part eq '') { $part='0'; } 5204: my $partgeneral=&EXT('resource.'.$part.'.'.$qualifierrest, 5205: $symbparm,$udom,$uname,$section,1); 5206: if (defined($partgeneral)) { return $partgeneral; } 5207: } 5208: if ($recurse) { return undef; } 5209: my $pack_def=&packages_tab_default($filename,$varname); 5210: if (defined($pack_def)) { return $pack_def; } 5211: 5212: # ---------------------------------------------------- Any other user namespace 5213: } elsif ($realm eq 'environment') { 5214: # ----------------------------------------------------------------- environment 5215: if (($uname eq $env{'user.name'})&&($udom eq $env{'user.domain'})) { 5216: return $env{'environment.'.$spacequalifierrest}; 5217: } else { 5218: my %returnhash=&userenvironment($udom,$uname, 5219: $spacequalifierrest); 5220: return $returnhash{$spacequalifierrest}; 5221: } 5222: } elsif ($realm eq 'system') { 5223: # ----------------------------------------------------------------- system.time 5224: if ($space eq 'time') { 5225: return time; 5226: } 5227: } elsif ($realm eq 'server') { 5228: # ----------------------------------------------------------------- system.time 5229: if ($space eq 'name') { 5230: return $ENV{'SERVER_NAME'}; 5231: } 5232: } 5233: return ''; 5234: } 5235: 5236: sub check_group_parms { 5237: my ($courseid,$groups,$symbparm,$mapparm,$what) = @_; 5238: my @groupitems = (); 5239: my $resultitem; 5240: my @levels = ($symbparm,$mapparm,$what); 5241: foreach my $group (@{$groups}) { 5242: foreach my $level (@levels) { 5243: my $item = $courseid.'.['.$group.'].'.$level; 5244: push(@groupitems,$item); 5245: } 5246: } 5247: my $coursereply = &resdata($env{'course.'.$courseid.'.num'}, 5248: $env{'course.'.$courseid.'.domain'}, 5249: 'course',@groupitems); 5250: return $coursereply; 5251: } 5252: 5253: sub sort_course_groups { # Sort groups based on defined rankings. Default is sort(). 5254: my ($grouplist,$courseid) = @_; 5255: my @groups = sort(split(/:/,$grouplist)); 5256: return @groups; 5257: } 5258: 5259: sub packages_tab_default { 5260: my ($uri,$varname)=@_; 5261: my (undef,$part,$name)=split(/\./,$varname); 5262: my $packages=&metadata($uri,'packages'); 5263: foreach my $package (split(/,/,$packages)) { 5264: my ($pack_type,$pack_part)=split(/_/,$package,2); 5265: if (defined($packagetab{"$pack_type&$name&default"})) { 5266: return $packagetab{"$pack_type&$name&default"}; 5267: } 5268: if ($pack_type eq 'part') { $pack_part='0'; } 5269: if (defined($packagetab{$pack_type."_".$pack_part."&$name&default"})) { 5270: return $packagetab{$pack_type."_".$pack_part."&$name&default"}; 5271: } 5272: } 5273: return undef; 5274: } 5275: 5276: sub add_prefix_and_part { 5277: my ($prefix,$part)=@_; 5278: my $keyroot; 5279: if (defined($prefix) && $prefix !~ /^__/) { 5280: # prefix that has a part already 5281: $keyroot=$prefix; 5282: } elsif (defined($prefix)) { 5283: # prefix that is missing a part 5284: if (defined($part)) { $keyroot='_'.$part.substr($prefix,1); } 5285: } else { 5286: # no prefix at all 5287: if (defined($part)) { $keyroot='_'.$part; } 5288: } 5289: return $keyroot; 5290: } 5291: 5292: # ---------------------------------------------------------------- Get metadata 5293: 5294: my %metaentry; 5295: sub metadata { 5296: my ($uri,$what,$liburi,$prefix,$depthcount)=@_; 5297: $uri=&declutter($uri); 5298: # if it is a non metadata possible uri return quickly 5299: if (($uri eq '') || 5300: (($uri =~ m|^/*adm/|) && 5301: ($uri !~ m|^adm/includes|) && ($uri !~ m|/bulletinboard$|)) || 5302: ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ /^~/) || 5303: ($uri =~ m|home/[^/]+/public_html/|)) { 5304: return undef; 5305: } 5306: my $filename=$uri; 5307: $uri=~s/\.meta$//; 5308: # 5309: # Is the metadata already cached? 5310: # Look at timestamp of caching 5311: # Everything is cached by the main uri, libraries are never directly cached 5312: # 5313: if (!defined($liburi)) { 5314: my ($result,$cached)=&is_cached_new('meta',$uri); 5315: if (defined($cached)) { return $result->{':'.$what}; } 5316: } 5317: { 5318: # 5319: # Is this a recursive call for a library? 5320: # 5321: # if (! exists($metacache{$uri})) { 5322: # $metacache{$uri}={}; 5323: # } 5324: if ($liburi) { 5325: $liburi=&declutter($liburi); 5326: $filename=$liburi; 5327: } else { 5328: &devalidate_cache_new('meta',$uri); 5329: undef(%metaentry); 5330: } 5331: my %metathesekeys=(); 5332: unless ($filename=~/\.meta$/) { $filename.='.meta'; } 5333: my $metastring; 5334: if ($uri !~ m -^(uploaded|editupload)/-) { 5335: my $file=&filelocation('',&clutter($filename)); 5336: #push(@{$metaentry{$uri.'.file'}},$file); 5337: $metastring=&getfile($file); 5338: } 5339: my $parser=HTML::LCParser->new(\$metastring); 5340: my $token; 5341: undef %metathesekeys; 5342: while ($token=$parser->get_token) { 5343: if ($token->[0] eq 'S') { 5344: if (defined($token->[2]->{'package'})) { 5345: # 5346: # This is a package - get package info 5347: # 5348: my $package=$token->[2]->{'package'}; 5349: my $keyroot=&add_prefix_and_part($prefix,$token->[2]->{'part'}); 5350: if (defined($token->[2]->{'id'})) { 5351: $keyroot.='_'.$token->[2]->{'id'}; 5352: } 5353: if ($metaentry{':packages'}) { 5354: $metaentry{':packages'}.=','.$package.$keyroot; 5355: } else { 5356: $metaentry{':packages'}=$package.$keyroot; 5357: } 5358: foreach (sort keys %packagetab) { 5359: my $part=$keyroot; 5360: $part=~s/^\_//; 5361: if ($_=~/^\Q$package\E\&/ || 5362: $_=~/^\Q$package\E_0\&/) { 5363: my ($pack,$name,$subp)=split(/\&/,$_); 5364: # ignore package.tab specified default values 5365: # here &package_tab_default() will fetch those 5366: if ($subp eq 'default') { next; } 5367: my $value=$packagetab{$_}; 5368: my $unikey; 5369: if ($pack =~ /_0$/) { 5370: $unikey='parameter_0_'.$name; 5371: $part=0; 5372: } else { 5373: $unikey='parameter'.$keyroot.'_'.$name; 5374: } 5375: if ($subp eq 'display') { 5376: $value.=' [Part: '.$part.']'; 5377: } 5378: $metaentry{':'.$unikey.'.part'}=$part; 5379: $metathesekeys{$unikey}=1; 5380: unless (defined($metaentry{':'.$unikey.'.'.$subp})) { 5381: $metaentry{':'.$unikey.'.'.$subp}=$value; 5382: } 5383: if (defined($metaentry{':'.$unikey.'.default'})) { 5384: $metaentry{':'.$unikey}= 5385: $metaentry{':'.$unikey.'.default'}; 5386: } 5387: } 5388: } 5389: } else { 5390: # 5391: # This is not a package - some other kind of start tag 5392: # 5393: my $entry=$token->[1]; 5394: my $unikey; 5395: if ($entry eq 'import') { 5396: $unikey=''; 5397: } else { 5398: $unikey=$entry; 5399: } 5400: $unikey.=&add_prefix_and_part($prefix,$token->[2]->{'part'}); 5401: 5402: if (defined($token->[2]->{'id'})) { 5403: $unikey.='_'.$token->[2]->{'id'}; 5404: } 5405: 5406: if ($entry eq 'import') { 5407: # 5408: # Importing a library here 5409: # 5410: if ($depthcount<20) { 5411: my $location=$parser->get_text('/import'); 5412: my $dir=$filename; 5413: $dir=~s|[^/]*$||; 5414: $location=&filelocation($dir,$location); 5415: foreach (sort(split(/\,/,&metadata($uri,'keys', 5416: $location,$unikey, 5417: $depthcount+1)))) { 5418: $metaentry{':'.$_}=$metaentry{':'.$_}; 5419: $metathesekeys{$_}=1; 5420: } 5421: } 5422: } else { 5423: 5424: if (defined($token->[2]->{'name'})) { 5425: $unikey.='_'.$token->[2]->{'name'}; 5426: } 5427: $metathesekeys{$unikey}=1; 5428: foreach (@{$token->[3]}) { 5429: $metaentry{':'.$unikey.'.'.$_}=$token->[2]->{$_}; 5430: } 5431: my $internaltext=&HTML::Entities::decode($parser->get_text('/'.$entry)); 5432: my $default=$metaentry{':'.$unikey.'.default'}; 5433: if ( $internaltext =~ /^\s*$/ && $default !~ /^\s*$/) { 5434: # only ws inside the tag, and not in default, so use default 5435: # as value 5436: $metaentry{':'.$unikey}=$default; 5437: } else { 5438: # either something interesting inside the tag or default 5439: # uninteresting 5440: $metaentry{':'.$unikey}=$internaltext; 5441: } 5442: # end of not-a-package not-a-library import 5443: } 5444: # end of not-a-package start tag 5445: } 5446: # the next is the end of "start tag" 5447: } 5448: } 5449: my ($extension) = ($uri =~ /\.(\w+)$/); 5450: foreach my $key (sort(keys(%packagetab))) { 5451: #no specific packages #how's our extension 5452: if ($key!~/^extension_\Q$extension\E&/) { next; } 5453: &metadata_create_package_def($uri,$key,'extension_'.$extension, 5454: \%metathesekeys); 5455: } 5456: if (!exists($metaentry{':packages'})) { 5457: foreach my $key (sort(keys(%packagetab))) { 5458: #no specific packages well let's get default then 5459: if ($key!~/^default&/) { next; } 5460: &metadata_create_package_def($uri,$key,'default', 5461: \%metathesekeys); 5462: } 5463: } 5464: # are there custom rights to evaluate 5465: if ($metaentry{':copyright'} eq 'custom') { 5466: 5467: # 5468: # Importing a rights file here 5469: # 5470: unless ($depthcount) { 5471: my $location=$metaentry{':customdistributionfile'}; 5472: my $dir=$filename; 5473: $dir=~s|[^/]*$||; 5474: $location=&filelocation($dir,$location); 5475: foreach (sort(split(/\,/,&metadata($uri,'keys', 5476: $location,'_rights', 5477: $depthcount+1)))) { 5478: #$metaentry{':'.$_}=$metacache{$uri}->{':'.$_}; 5479: $metathesekeys{$_}=1; 5480: } 5481: } 5482: } 5483: $metaentry{':keys'}=join(',',keys %metathesekeys); 5484: &metadata_generate_part0(\%metathesekeys,\%metaentry,$uri); 5485: $metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys); 5486: &do_cache_new('meta',$uri,\%metaentry,60*60); 5487: # this is the end of "was not already recently cached 5488: } 5489: return $metaentry{':'.$what}; 5490: } 5491: 5492: sub metadata_create_package_def { 5493: my ($uri,$key,$package,$metathesekeys)=@_; 5494: my ($pack,$name,$subp)=split(/\&/,$key); 5495: if ($subp eq 'default') { next; } 5496: 5497: if (defined($metaentry{':packages'})) { 5498: $metaentry{':packages'}.=','.$package; 5499: } else { 5500: $metaentry{':packages'}=$package; 5501: } 5502: my $value=$packagetab{$key}; 5503: my $unikey; 5504: $unikey='parameter_0_'.$name; 5505: $metaentry{':'.$unikey.'.part'}=0; 5506: $$metathesekeys{$unikey}=1; 5507: unless (defined($metaentry{':'.$unikey.'.'.$subp})) { 5508: $metaentry{':'.$unikey.'.'.$subp}=$value; 5509: } 5510: if (defined($metaentry{':'.$unikey.'.default'})) { 5511: $metaentry{':'.$unikey}= 5512: $metaentry{':'.$unikey.'.default'}; 5513: } 5514: } 5515: 5516: sub metadata_generate_part0 { 5517: my ($metadata,$metacache,$uri) = @_; 5518: my %allnames; 5519: foreach my $metakey (sort keys %$metadata) { 5520: if ($metakey=~/^parameter\_(.*)/) { 5521: my $part=$$metacache{':'.$metakey.'.part'}; 5522: my $name=$$metacache{':'.$metakey.'.name'}; 5523: if (! exists($$metadata{'parameter_0_'.$name.'.name'})) { 5524: $allnames{$name}=$part; 5525: } 5526: } 5527: } 5528: foreach my $name (keys(%allnames)) { 5529: $$metadata{"parameter_0_$name"}=1; 5530: my $key=":parameter_0_$name"; 5531: $$metacache{"$key.part"}='0'; 5532: $$metacache{"$key.name"}=$name; 5533: $$metacache{"$key.type"}=$$metacache{':parameter_'. 5534: $allnames{$name}.'_'.$name. 5535: '.type'}; 5536: my $olddis=$$metacache{':parameter_'.$allnames{$name}.'_'.$name. 5537: '.display'}; 5538: my $expr='[Part: '.$allnames{$name}.']'; 5539: $olddis=~s/\Q$expr\E/\[Part: 0\]/; 5540: $$metacache{"$key.display"}=$olddis; 5541: } 5542: } 5543: 5544: # ------------------------------------------------- Get the title of a resource 5545: 5546: sub gettitle { 5547: my $urlsymb=shift; 5548: my $symb=&symbread($urlsymb); 5549: if ($symb) { 5550: my $key=$env{'request.course.id'}."\0".$symb; 5551: my ($result,$cached)=&is_cached_new('title',$key); 5552: if (defined($cached)) { 5553: return $result; 5554: } 5555: my ($map,$resid,$url)=&decode_symb($symb); 5556: my $title=''; 5557: my %bighash; 5558: if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db', 5559: &GDBM_READER(),0640)) { 5560: my $mapid=$bighash{'map_pc_'.&clutter($map)}; 5561: $title=$bighash{'title_'.$mapid.'.'.$resid}; 5562: untie %bighash; 5563: } 5564: $title=~s/\&colon\;/\:/gs; 5565: if ($title) { 5566: return &do_cache_new('title',$key,$title,600); 5567: } 5568: $urlsymb=$url; 5569: } 5570: my $title=&metadata($urlsymb,'title'); 5571: if (!$title) { $title=(split('/',$urlsymb))[-1]; } 5572: return $title; 5573: } 5574: 5575: sub get_slot { 5576: my ($which,$cnum,$cdom)=@_; 5577: if (!$cnum || !$cdom) { 5578: (undef,my $courseid)=&Apache::lonxml::whichuser(); 5579: $cdom=$env{'course.'.$courseid.'.domain'}; 5580: $cnum=$env{'course.'.$courseid.'.num'}; 5581: } 5582: my $key=join("\0",'slots',$cdom,$cnum,$which); 5583: my %slotinfo; 5584: if (exists($remembered{$key})) { 5585: $slotinfo{$which} = $remembered{$key}; 5586: } else { 5587: %slotinfo=&get('slots',[$which],$cdom,$cnum); 5588: &Apache::lonhomework::showhash(%slotinfo); 5589: my ($tmp)=keys(%slotinfo); 5590: if ($tmp=~/^error:/) { return (); } 5591: $remembered{$key} = $slotinfo{$which}; 5592: } 5593: if (ref($slotinfo{$which}) eq 'HASH') { 5594: return %{$slotinfo{$which}}; 5595: } 5596: return $slotinfo{$which}; 5597: } 5598: # ------------------------------------------------- Update symbolic store links 5599: 5600: sub symblist { 5601: my ($mapname,%newhash)=@_; 5602: $mapname=&deversion(&declutter($mapname)); 5603: my %hash; 5604: if (($env{'request.course.fn'}) && (%newhash)) { 5605: if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', 5606: &GDBM_WRCREAT(),0640)) { 5607: foreach my $url (keys %newhash) { 5608: next if ($url eq 'last_known' 5609: && $env{'form.no_update_last_known'}); 5610: $hash{declutter($url)}=&encode_symb($mapname, 5611: $newhash{$url}->[1], 5612: $newhash{$url}->[0]); 5613: } 5614: if (untie(%hash)) { 5615: return 'ok'; 5616: } 5617: } 5618: } 5619: return 'error'; 5620: } 5621: 5622: # --------------------------------------------------------------- Verify a symb 5623: 5624: sub symbverify { 5625: my ($symb,$thisurl)=@_; 5626: my $thisfn=$thisurl; 5627: # wrapper not part of symbs 5628: $thisfn=~s/^\/adm\/wrapper//; 5629: $thisfn=~s/^\/adm\/coursedocs\/showdoc\///; 5630: $thisfn=&declutter($thisfn); 5631: # direct jump to resource in page or to a sequence - will construct own symbs 5632: if ($thisfn=~/\.(page|sequence)$/) { return 1; } 5633: # check URL part 5634: my ($map,$resid,$url)=&decode_symb($symb); 5635: 5636: unless ($url eq $thisfn) { return 0; } 5637: 5638: $symb=&symbclean($symb); 5639: $thisurl=&deversion($thisurl); 5640: $thisfn=&deversion($thisfn); 5641: 5642: my %bighash; 5643: my $okay=0; 5644: 5645: if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db', 5646: &GDBM_READER(),0640)) { 5647: my $ids=$bighash{'ids_'.&clutter($thisurl)}; 5648: unless ($ids) { 5649: $ids=$bighash{'ids_/'.$thisurl}; 5650: } 5651: if ($ids) { 5652: # ------------------------------------------------------------------- Has ID(s) 5653: foreach (split(/\,/,$ids)) { 5654: my ($mapid,$resid)=split(/\./,$_); 5655: if ( 5656: &symbclean(&declutter($bighash{'map_id_'.$mapid}).'___'.$resid.'___'.$thisfn) 5657: eq $symb) { 5658: if (($env{'request.role.adv'}) || 5659: $bighash{'encrypted_'.$_} eq $env{'request.enc'}) { 5660: $okay=1; 5661: } 5662: } 5663: } 5664: } 5665: untie(%bighash); 5666: } 5667: return $okay; 5668: } 5669: 5670: # --------------------------------------------------------------- Clean-up symb 5671: 5672: sub symbclean { 5673: my $symb=shift; 5674: if ($symb=~m|^/enc/|) { $symb=&Apache::lonenc::unencrypted($symb); } 5675: # remove version from map 5676: $symb=~s/\.(\d+)\.(\w+)\_\_\_/\.$2\_\_\_/; 5677: 5678: # remove version from URL 5679: $symb=~s/\.(\d+)\.(\w+)$/\.$2/; 5680: 5681: # remove wrapper 5682: 5683: $symb=~s/(\_\_\_\d+\_\_\_)adm\/wrapper\/(res\/)*/$1/; 5684: $symb=~s/(\_\_\_\d+\_\_\_)adm\/coursedocs\/showdoc\/(res\/)*/$1/; 5685: return $symb; 5686: } 5687: 5688: # ---------------------------------------------- Split symb to find map and url 5689: 5690: sub encode_symb { 5691: my ($map,$resid,$url)=@_; 5692: return &symbclean(&declutter($map).'___'.$resid.'___'.&declutter($url)); 5693: } 5694: 5695: sub decode_symb { 5696: my $symb=shift; 5697: if ($symb=~m|^/enc/|) { $symb=&Apache::lonenc::unencrypted($symb); } 5698: my ($map,$resid,$url)=split(/___/,$symb); 5699: return (&fixversion($map),$resid,&fixversion($url)); 5700: } 5701: 5702: sub fixversion { 5703: my $fn=shift; 5704: if ($fn=~/^(adm|uploaded|editupload|public)/) { return $fn; } 5705: my %bighash; 5706: my $uri=&clutter($fn); 5707: my $key=$env{'request.course.id'}.'_'.$uri; 5708: # is this cached? 5709: my ($result,$cached)=&is_cached_new('courseresversion',$key); 5710: if (defined($cached)) { return $result; } 5711: # unfortunately not cached, or expired 5712: if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db', 5713: &GDBM_READER(),0640)) { 5714: if ($bighash{'version_'.$uri}) { 5715: my $version=$bighash{'version_'.$uri}; 5716: unless (($version eq 'mostrecent') || 5717: ($version==&getversion($uri))) { 5718: $uri=~s/\.(\w+)$/\.$version\.$1/; 5719: } 5720: } 5721: untie %bighash; 5722: } 5723: return &do_cache_new('courseresversion',$key,&declutter($uri),600); 5724: } 5725: 5726: sub deversion { 5727: my $url=shift; 5728: $url=~s/\.\d+\.(\w+)$/\.$1/; 5729: return $url; 5730: } 5731: 5732: # ------------------------------------------------------ Return symb list entry 5733: 5734: sub symbread { 5735: my ($thisfn,$donotrecurse)=@_; 5736: my $cache_str='request.symbread.cached.'.$thisfn; 5737: if (defined($env{$cache_str})) { return $env{$cache_str}; } 5738: # no filename provided? try from environment 5739: unless ($thisfn) { 5740: if ($env{'request.symb'}) { 5741: return $env{$cache_str}=&symbclean($env{'request.symb'}); 5742: } 5743: $thisfn=$env{'request.filename'}; 5744: } 5745: if ($thisfn=~m|^/enc/|) { $thisfn=&Apache::lonenc::unencrypted($thisfn); } 5746: # is that filename actually a symb? Verify, clean, and return 5747: if ($thisfn=~/\_\_\_\d+\_\_\_(.*)$/) { 5748: if (&symbverify($thisfn,$1)) { 5749: return $env{$cache_str}=&symbclean($thisfn); 5750: } 5751: } 5752: $thisfn=declutter($thisfn); 5753: my %hash; 5754: my %bighash; 5755: my $syval=''; 5756: if (($env{'request.course.fn'}) && ($thisfn)) { 5757: my $targetfn = $thisfn; 5758: if ( ($thisfn =~ m/^(uploaded|editupload)\//) && ($thisfn !~ m/\.(page|sequence)$/) ) { 5759: $targetfn = 'adm/wrapper/'.$thisfn; 5760: } 5761: if ($targetfn =~ m|^adm/wrapper/(ext/.*)|) { 5762: $targetfn=$1; 5763: } 5764: if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', 5765: &GDBM_READER(),0640)) { 5766: $syval=$hash{$targetfn}; 5767: untie(%hash); 5768: } 5769: # ---------------------------------------------------------- There was an entry 5770: if ($syval) { 5771: #unless ($syval=~/\_\d+$/) { 5772: #unless ($env{'form.request.prefix'}=~/\.(\d+)\_$/) { 5773: #&appenv('request.ambiguous' => $thisfn); 5774: #return $env{$cache_str}=''; 5775: #} 5776: #$syval.=$1; 5777: #} 5778: } else { 5779: # ------------------------------------------------------- Was not in symb table 5780: if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db', 5781: &GDBM_READER(),0640)) { 5782: # ---------------------------------------------- Get ID(s) for current resource 5783: my $ids=$bighash{'ids_'.&clutter($thisfn)}; 5784: unless ($ids) { 5785: $ids=$bighash{'ids_/'.$thisfn}; 5786: } 5787: unless ($ids) { 5788: # alias? 5789: $ids=$bighash{'mapalias_'.$thisfn}; 5790: } 5791: if ($ids) { 5792: # ------------------------------------------------------------------- Has ID(s) 5793: my @possibilities=split(/\,/,$ids); 5794: if ($#possibilities==0) { 5795: # ----------------------------------------------- There is only one possibility 5796: my ($mapid,$resid)=split(/\./,$ids); 5797: $syval=&encode_symb($bighash{'map_id_'.$mapid}, 5798: $resid,$thisfn); 5799: } elsif (!$donotrecurse) { 5800: # ------------------------------------------ There is more than one possibility 5801: my $realpossible=0; 5802: foreach (@possibilities) { 5803: my $file=$bighash{'src_'.$_}; 5804: if (&allowed('bre',$file)) { 5805: my ($mapid,$resid)=split(/\./,$_); 5806: if ($bighash{'map_type_'.$mapid} ne 'page') { 5807: $realpossible++; 5808: $syval=&encode_symb($bighash{'map_id_'.$mapid}, 5809: $resid,$thisfn); 5810: } 5811: } 5812: } 5813: if ($realpossible!=1) { $syval=''; } 5814: } else { 5815: $syval=''; 5816: } 5817: } 5818: untie(%bighash) 5819: } 5820: } 5821: if ($syval) { 5822: return $env{$cache_str}=$syval; 5823: } 5824: } 5825: &appenv('request.ambiguous' => $thisfn); 5826: return $env{$cache_str}=''; 5827: } 5828: 5829: # ---------------------------------------------------------- Return random seed 5830: 5831: sub numval { 5832: my $txt=shift; 5833: $txt=~tr/A-J/0-9/; 5834: $txt=~tr/a-j/0-9/; 5835: $txt=~tr/K-T/0-9/; 5836: $txt=~tr/k-t/0-9/; 5837: $txt=~tr/U-Z/0-5/; 5838: $txt=~tr/u-z/0-5/; 5839: $txt=~s/\D//g; 5840: if ($_64bit) { if ($txt > 2**32) { return -1; } } 5841: return int($txt); 5842: } 5843: 5844: sub numval2 { 5845: my $txt=shift; 5846: $txt=~tr/A-J/0-9/; 5847: $txt=~tr/a-j/0-9/; 5848: $txt=~tr/K-T/0-9/; 5849: $txt=~tr/k-t/0-9/; 5850: $txt=~tr/U-Z/0-5/; 5851: $txt=~tr/u-z/0-5/; 5852: $txt=~s/\D//g; 5853: my @txts=split(/(\d\d\d\d\d\d\d\d\d)/,$txt); 5854: my $total; 5855: foreach my $val (@txts) { $total+=$val; } 5856: if ($_64bit) { if ($total > 2**32) { return -1; } } 5857: return int($total); 5858: } 5859: 5860: sub numval3 { 5861: use integer; 5862: my $txt=shift; 5863: $txt=~tr/A-J/0-9/; 5864: $txt=~tr/a-j/0-9/; 5865: $txt=~tr/K-T/0-9/; 5866: $txt=~tr/k-t/0-9/; 5867: $txt=~tr/U-Z/0-5/; 5868: $txt=~tr/u-z/0-5/; 5869: $txt=~s/\D//g; 5870: my @txts=split(/(\d\d\d\d\d\d\d\d\d)/,$txt); 5871: my $total; 5872: foreach my $val (@txts) { $total+=$val; } 5873: if ($_64bit) { $total=(($total<<32)>>32); } 5874: return $total; 5875: } 5876: 5877: sub digest { 5878: my ($data)=@_; 5879: my $digest=&Digest::MD5::md5($data); 5880: my ($a,$b,$c,$d)=unpack("iiii",$digest); 5881: my ($e,$f); 5882: { 5883: use integer; 5884: $e=($a+$b); 5885: $f=($c+$d); 5886: if ($_64bit) { 5887: $e=(($e<<32)>>32); 5888: $f=(($f<<32)>>32); 5889: } 5890: } 5891: if (wantarray) { 5892: return ($e,$f); 5893: } else { 5894: my $g; 5895: { 5896: use integer; 5897: $g=($e+$f); 5898: if ($_64bit) { 5899: $g=(($g<<32)>>32); 5900: } 5901: } 5902: return $g; 5903: } 5904: } 5905: 5906: sub latest_rnd_algorithm_id { 5907: return '64bit5'; 5908: } 5909: 5910: sub get_rand_alg { 5911: my ($courseid)=@_; 5912: if (!$courseid) { $courseid=(&Apache::lonxml::whichuser())[1]; } 5913: if ($courseid) { 5914: return $env{"course.$courseid.rndseed"}; 5915: } 5916: return &latest_rnd_algorithm_id(); 5917: } 5918: 5919: sub validCODE { 5920: my ($CODE)=@_; 5921: if (defined($CODE) && $CODE ne '' && $CODE =~ /^\w+$/) { return 1; } 5922: return 0; 5923: } 5924: 5925: sub getCODE { 5926: if (&validCODE($env{'form.CODE'})) { return $env{'form.CODE'}; } 5927: if ( (defined($Apache::lonhomework::parsing_a_problem) || 5928: defined($Apache::lonhomework::parsing_a_task) ) && 5929: &validCODE($Apache::lonhomework::history{'resource.CODE'})) { 5930: return $Apache::lonhomework::history{'resource.CODE'}; 5931: } 5932: return undef; 5933: } 5934: 5935: sub rndseed { 5936: my ($symb,$courseid,$domain,$username)=@_; 5937: 5938: my ($wsymb,$wcourseid,$wdomain,$wusername)=&Apache::lonxml::whichuser(); 5939: if (!$symb) { 5940: unless ($symb=$wsymb) { return time; } 5941: } 5942: if (!$courseid) { $courseid=$wcourseid; } 5943: if (!$domain) { $domain=$wdomain; } 5944: if (!$username) { $username=$wusername } 5945: my $which=&get_rand_alg(); 5946: if (defined(&getCODE())) { 5947: if ($which eq '64bit5') { 5948: return &rndseed_CODE_64bit5($symb,$courseid,$domain,$username); 5949: } elsif ($which eq '64bit4') { 5950: return &rndseed_CODE_64bit4($symb,$courseid,$domain,$username); 5951: } else { 5952: return &rndseed_CODE_64bit($symb,$courseid,$domain,$username); 5953: } 5954: } elsif ($which eq '64bit5') { 5955: return &rndseed_64bit5($symb,$courseid,$domain,$username); 5956: } elsif ($which eq '64bit4') { 5957: return &rndseed_64bit4($symb,$courseid,$domain,$username); 5958: } elsif ($which eq '64bit3') { 5959: return &rndseed_64bit3($symb,$courseid,$domain,$username); 5960: } elsif ($which eq '64bit2') { 5961: return &rndseed_64bit2($symb,$courseid,$domain,$username); 5962: } elsif ($which eq '64bit') { 5963: return &rndseed_64bit($symb,$courseid,$domain,$username); 5964: } 5965: return &rndseed_32bit($symb,$courseid,$domain,$username); 5966: } 5967: 5968: sub rndseed_32bit { 5969: my ($symb,$courseid,$domain,$username)=@_; 5970: { 5971: use integer; 5972: my $symbchck=unpack("%32C*",$symb) << 27; 5973: my $symbseed=numval($symb) << 22; 5974: my $namechck=unpack("%32C*",$username) << 17; 5975: my $nameseed=numval($username) << 12; 5976: my $domainseed=unpack("%32C*",$domain) << 7; 5977: my $courseseed=unpack("%32C*",$courseid); 5978: my $num=$symbseed+$nameseed+$domainseed+$courseseed+$namechck+$symbchck; 5979: #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); 5980: #&Apache::lonxml::debug("rndseed :$num:$symb"); 5981: if ($_64bit) { $num=(($num<<32)>>32); } 5982: return $num; 5983: } 5984: } 5985: 5986: sub rndseed_64bit { 5987: my ($symb,$courseid,$domain,$username)=@_; 5988: { 5989: use integer; 5990: my $symbchck=unpack("%32S*",$symb) << 21; 5991: my $symbseed=numval($symb) << 10; 5992: my $namechck=unpack("%32S*",$username); 5993: 5994: my $nameseed=numval($username) << 21; 5995: my $domainseed=unpack("%32S*",$domain) << 10; 5996: my $courseseed=unpack("%32S*",$courseid); 5997: 5998: my $num1=$symbchck+$symbseed+$namechck; 5999: my $num2=$nameseed+$domainseed+$courseseed; 6000: #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); 6001: #&Apache::lonxml::debug("rndseed :$num:$symb"); 6002: if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } 6003: if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } 6004: return "$num1,$num2"; 6005: } 6006: } 6007: 6008: sub rndseed_64bit2 { 6009: my ($symb,$courseid,$domain,$username)=@_; 6010: { 6011: use integer; 6012: # strings need to be an even # of cahracters long, it it is odd the 6013: # last characters gets thrown away 6014: my $symbchck=unpack("%32S*",$symb.' ') << 21; 6015: my $symbseed=numval($symb) << 10; 6016: my $namechck=unpack("%32S*",$username.' '); 6017: 6018: my $nameseed=numval($username) << 21; 6019: my $domainseed=unpack("%32S*",$domain.' ') << 10; 6020: my $courseseed=unpack("%32S*",$courseid.' '); 6021: 6022: my $num1=$symbchck+$symbseed+$namechck; 6023: my $num2=$nameseed+$domainseed+$courseseed; 6024: #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); 6025: #&Apache::lonxml::debug("rndseed :$num:$symb"); 6026: return "$num1,$num2"; 6027: } 6028: } 6029: 6030: sub rndseed_64bit3 { 6031: my ($symb,$courseid,$domain,$username)=@_; 6032: { 6033: use integer; 6034: # strings need to be an even # of cahracters long, it it is odd the 6035: # last characters gets thrown away 6036: my $symbchck=unpack("%32S*",$symb.' ') << 21; 6037: my $symbseed=numval2($symb) << 10; 6038: my $namechck=unpack("%32S*",$username.' '); 6039: 6040: my $nameseed=numval2($username) << 21; 6041: my $domainseed=unpack("%32S*",$domain.' ') << 10; 6042: my $courseseed=unpack("%32S*",$courseid.' '); 6043: 6044: my $num1=$symbchck+$symbseed+$namechck; 6045: my $num2=$nameseed+$domainseed+$courseseed; 6046: #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); 6047: #&Apache::lonxml::debug("rndseed :$num1:$num2:$_64bit"); 6048: if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } 6049: 6050: return "$num1:$num2"; 6051: } 6052: } 6053: 6054: sub rndseed_64bit4 { 6055: my ($symb,$courseid,$domain,$username)=@_; 6056: { 6057: use integer; 6058: # strings need to be an even # of cahracters long, it it is odd the 6059: # last characters gets thrown away 6060: my $symbchck=unpack("%32S*",$symb.' ') << 21; 6061: my $symbseed=numval3($symb) << 10; 6062: my $namechck=unpack("%32S*",$username.' '); 6063: 6064: my $nameseed=numval3($username) << 21; 6065: my $domainseed=unpack("%32S*",$domain.' ') << 10; 6066: my $courseseed=unpack("%32S*",$courseid.' '); 6067: 6068: my $num1=$symbchck+$symbseed+$namechck; 6069: my $num2=$nameseed+$domainseed+$courseseed; 6070: #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck"); 6071: #&Apache::lonxml::debug("rndseed :$num1:$num2:$_64bit"); 6072: if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); } 6073: 6074: return "$num1:$num2"; 6075: } 6076: } 6077: 6078: sub rndseed_64bit5 { 6079: my ($symb,$courseid,$domain,$username)=@_; 6080: my ($num1,$num2)=&digest("$symb,$courseid,$domain,$username"); 6081: return "$num1:$num2"; 6082: } 6083: 6084: sub rndseed_CODE_64bit { 6085: my ($symb,$courseid,$domain,$username)=@_; 6086: { 6087: use integer; 6088: my $symbchck=unpack("%32S*",$symb.' ') << 16; 6089: my $symbseed=numval2($symb); 6090: my $CODEchck=unpack("%32S*",&getCODE().' ') << 16; 6091: my $CODEseed=numval(&getCODE()); 6092: my $courseseed=unpack("%32S*",$courseid.' '); 6093: my $num1=$symbseed+$CODEchck; 6094: my $num2=$CODEseed+$courseseed+$symbchck; 6095: #&Apache::lonxml::debug("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck"); 6096: #&Apache::lonxml::debug("rndseed :$num1:$num2:$symb"); 6097: if ($_64bit) { $num1=(($num1<<32)>>32); } 6098: if ($_64bit) { $num2=(($num2<<32)>>32); } 6099: return "$num1:$num2"; 6100: } 6101: } 6102: 6103: sub rndseed_CODE_64bit4 { 6104: my ($symb,$courseid,$domain,$username)=@_; 6105: { 6106: use integer; 6107: my $symbchck=unpack("%32S*",$symb.' ') << 16; 6108: my $symbseed=numval3($symb); 6109: my $CODEchck=unpack("%32S*",&getCODE().' ') << 16; 6110: my $CODEseed=numval3(&getCODE()); 6111: my $courseseed=unpack("%32S*",$courseid.' '); 6112: my $num1=$symbseed+$CODEchck; 6113: my $num2=$CODEseed+$courseseed+$symbchck; 6114: #&Apache::lonxml::debug("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck"); 6115: #&Apache::lonxml::debug("rndseed :$num1:$num2:$symb"); 6116: if ($_64bit) { $num1=(($num1<<32)>>32); } 6117: if ($_64bit) { $num2=(($num2<<32)>>32); } 6118: return "$num1:$num2"; 6119: } 6120: } 6121: 6122: sub rndseed_CODE_64bit5 { 6123: my ($symb,$courseid,$domain,$username)=@_; 6124: my $code = &getCODE(); 6125: my ($num1,$num2)=&digest("$symb,$courseid,$code"); 6126: return "$num1:$num2"; 6127: } 6128: 6129: sub setup_random_from_rndseed { 6130: my ($rndseed)=@_; 6131: if ($rndseed =~/([,:])/) { 6132: my ($num1,$num2)=split(/[,:]/,$rndseed); 6133: &Math::Random::random_set_seed(abs($num1),abs($num2)); 6134: } else { 6135: &Math::Random::random_set_seed_from_phrase($rndseed); 6136: } 6137: } 6138: 6139: sub latest_receipt_algorithm_id { 6140: return 'receipt2'; 6141: } 6142: 6143: sub recunique { 6144: my $fucourseid=shift; 6145: my $unique; 6146: if ($env{"course.$fucourseid.receiptalg"} eq 'receipt2') { 6147: $unique=$env{"course.$fucourseid.internal.encseed"}; 6148: } else { 6149: $unique=$perlvar{'lonReceipt'}; 6150: } 6151: return unpack("%32C*",$unique); 6152: } 6153: 6154: sub recprefix { 6155: my $fucourseid=shift; 6156: my $prefix; 6157: if ($env{"course.$fucourseid.receiptalg"} eq 'receipt2') { 6158: $prefix=$env{"course.$fucourseid.internal.encpref"}; 6159: } else { 6160: $prefix=$perlvar{'lonHostID'}; 6161: } 6162: return unpack("%32C*",$prefix); 6163: } 6164: 6165: sub ireceipt { 6166: my ($funame,$fudom,$fucourseid,$fusymb,$part)=@_; 6167: my $cuname=unpack("%32C*",$funame); 6168: my $cudom=unpack("%32C*",$fudom); 6169: my $cucourseid=unpack("%32C*",$fucourseid); 6170: my $cusymb=unpack("%32C*",$fusymb); 6171: my $cunique=&recunique($fucourseid); 6172: my $cpart=unpack("%32S*",$part); 6173: my $return =&recprefix($fucourseid).'-'; 6174: if ($env{"course.$fucourseid.receiptalg"} eq 'receipt2' || 6175: $env{'request.state'} eq 'construct') { 6176: &Apache::lonxml::debug("doing receipt2 using parts $cpart, uname $cuname and udom $cudom gets ".($cpart%$cuname). 6177: " and ".($cpart%$cudom)); 6178: 6179: $return.= ($cunique%$cuname+ 6180: $cunique%$cudom+ 6181: $cusymb%$cuname+ 6182: $cusymb%$cudom+ 6183: $cucourseid%$cuname+ 6184: $cucourseid%$cudom+ 6185: $cpart%$cuname+ 6186: $cpart%$cudom); 6187: } else { 6188: $return.= ($cunique%$cuname+ 6189: $cunique%$cudom+ 6190: $cusymb%$cuname+ 6191: $cusymb%$cudom+ 6192: $cucourseid%$cuname+ 6193: $cucourseid%$cudom); 6194: } 6195: return $return; 6196: } 6197: 6198: sub receipt { 6199: my ($part)=@_; 6200: my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser(); 6201: return &ireceipt($name,$domain,$courseid,$symb,$part); 6202: } 6203: 6204: # ------------------------------------------------------------ Serves up a file 6205: # returns either the contents of the file or 6206: # -1 if the file doesn't exist 6207: # 6208: # if the target is a file that was uploaded via DOCS, 6209: # a check will be made to see if a current copy exists on the local server, 6210: # if it does this will be served, otherwise a copy will be retrieved from 6211: # the home server for the course and stored in /home/httpd/html/userfiles on 6212: # the local server. 6213: 6214: sub getfile { 6215: my ($file) = @_; 6216: if ($file =~ m -^/*(uploaded|editupload)/-) { $file=&filelocation("",$file); } 6217: &repcopy($file); 6218: return &readfile($file); 6219: } 6220: 6221: sub repcopy_userfile { 6222: my ($file)=@_; 6223: if ($file =~ m -^/*(uploaded|editupload)/-) { $file=&filelocation("",$file); } 6224: if ($file =~ m|^/home/httpd/html/lonUsers/|) { return 'ok'; } 6225: my ($cdom,$cnum,$filename) = 6226: ($file=~m|^\Q$perlvar{'lonDocRoot'}\E/+userfiles/+([^/]+)/+([^/]+)/+(.*)|); 6227: my ($info,$rtncode); 6228: my $uri="/uploaded/$cdom/$cnum/$filename"; 6229: if (-e "$file") { 6230: my @fileinfo = stat($file); 6231: my $lwpresp = &getuploaded('HEAD',$uri,$cdom,$cnum,\$info,\$rtncode); 6232: if ($lwpresp ne 'ok') { 6233: if ($rtncode eq '404') { 6234: unlink($file); 6235: } 6236: #my $ua=new LWP::UserAgent; 6237: #my $request=new HTTP::Request('GET',&tokenwrapper($uri)); 6238: #my $response=$ua->request($request); 6239: #if ($response->is_success()) { 6240: # return $response->content; 6241: # } else { 6242: # return -1; 6243: # } 6244: return -1; 6245: } 6246: if ($info < $fileinfo[9]) { 6247: return 'ok'; 6248: } 6249: $info = ''; 6250: $lwpresp = &getuploaded('GET',$uri,$cdom,$cnum,\$info,\$rtncode); 6251: if ($lwpresp ne 'ok') { 6252: return -1; 6253: } 6254: } else { 6255: my $lwpresp = &getuploaded('GET',$uri,$cdom,$cnum,\$info,\$rtncode); 6256: if ($lwpresp ne 'ok') { 6257: my $ua=new LWP::UserAgent; 6258: my $request=new HTTP::Request('GET',&tokenwrapper($uri)); 6259: my $response=$ua->request($request); 6260: if ($response->is_success()) { 6261: $info=$response->content; 6262: } else { 6263: return -1; 6264: } 6265: } 6266: my @parts = ($cdom,$cnum); 6267: if ($filename =~ m|^(.+)/[^/]+$|) { 6268: push @parts, split(/\//,$1); 6269: } 6270: my $path = $perlvar{'lonDocRoot'}.'/userfiles'; 6271: foreach my $part (@parts) { 6272: $path .= '/'.$part; 6273: if (!-e $path) { 6274: mkdir($path,0770); 6275: } 6276: } 6277: } 6278: open(FILE,">$file"); 6279: print FILE $info; 6280: close(FILE); 6281: return 'ok'; 6282: } 6283: 6284: sub tokenwrapper { 6285: my $uri=shift; 6286: $uri=~s|^http\://([^/]+)||; 6287: $uri=~s|^/||; 6288: $env{'user.environment'}=~/\/([^\/]+)\.id/; 6289: my $token=$1; 6290: my (undef,$udom,$uname,$file)=split('/',$uri,4); 6291: if ($udom && $uname && $file) { 6292: $file=~s|(\?\.*)*$||; 6293: &appenv("userfile.$udom/$uname/$file" => $env{'request.course.id'}); 6294: return 'http://'.$hostname{ &homeserver($uname,$udom)}.'/'.$uri. 6295: (($uri=~/\?/)?'&':'?').'token='.$token. 6296: '&tokenissued='.$perlvar{'lonHostID'}; 6297: } else { 6298: return '/adm/notfound.html'; 6299: } 6300: } 6301: 6302: sub getuploaded { 6303: my ($reqtype,$uri,$cdom,$cnum,$info,$rtncode) = @_; 6304: $uri=~s/^\///; 6305: $uri = 'http://'.$hostname{ &homeserver($cnum,$cdom)}.'/raw/'.$uri; 6306: my $ua=new LWP::UserAgent; 6307: my $request=new HTTP::Request($reqtype,$uri); 6308: my $response=$ua->request($request); 6309: $$rtncode = $response->code; 6310: if (! $response->is_success()) { 6311: return 'failed'; 6312: } 6313: if ($reqtype eq 'HEAD') { 6314: $$info = &HTTP::Date::str2time( $response->header('Last-modified') ); 6315: } elsif ($reqtype eq 'GET') { 6316: $$info = $response->content; 6317: } 6318: return 'ok'; 6319: } 6320: 6321: sub readfile { 6322: my $file = shift; 6323: if ( (! -e $file ) || ($file eq '') ) { return -1; }; 6324: my $fh; 6325: open($fh,"<$file"); 6326: my $a=''; 6327: while (<$fh>) { $a .=$_; } 6328: return $a; 6329: } 6330: 6331: sub filelocation { 6332: my ($dir,$file) = @_; 6333: my $location; 6334: $file=~ s/^\s*(\S+)\s*$/$1/; ## strip off leading and trailing spaces 6335: 6336: if ($file =~ m-^/adm/-) { 6337: $file=~s-^/adm/wrapper/-/-; 6338: $file=~s-^/adm/coursedocs/showdoc/-/-; 6339: } 6340: if ($file=~m:^/~:) { # is a contruction space reference 6341: $location = $file; 6342: $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:; 6343: } elsif ($file=~m:^/home/[^/]*/public_html/:) { 6344: # is a correct contruction space reference 6345: $location = $file; 6346: } elsif ($file=~/^\/*(uploaded|editupload)/) { # is an uploaded file 6347: my ($udom,$uname,$filename)= 6348: ($file=~m -^/+(?:uploaded|editupload)/+([^/]+)/+([^/]+)/+(.*)$-); 6349: my $home=&homeserver($uname,$udom); 6350: my $is_me=0; 6351: my @ids=¤t_machine_ids(); 6352: foreach my $id (@ids) { if ($id eq $home) { $is_me=1; } } 6353: if ($is_me) { 6354: $location=&Apache::loncommon::propath($udom,$uname). 6355: '/userfiles/'.$filename; 6356: } else { 6357: $location=$Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles/'. 6358: $udom.'/'.$uname.'/'.$filename; 6359: } 6360: } else { 6361: $file=~s/^\Q$perlvar{'lonDocRoot'}\E//; 6362: $file=~s:^/res/:/:; 6363: if ( !( $file =~ m:^/:) ) { 6364: $location = $dir. '/'.$file; 6365: } else { 6366: $location = '/home/httpd/html/res'.$file; 6367: } 6368: } 6369: $location=~s://+:/:g; # remove duplicate / 6370: while ($location=~m:/\.\./:) {$location=~ s:/[^/]+/\.\./:/:g;} #remove dir/.. 6371: while ($location=~m:/\./:) {$location=~ s:/\./:/:g;} #remove /./ 6372: return $location; 6373: } 6374: 6375: sub hreflocation { 6376: my ($dir,$file)=@_; 6377: unless (($file=~m-^http://-i) || ($file=~m-^/-)) { 6378: $file=filelocation($dir,$file); 6379: } elsif ($file=~m-^/adm/-) { 6380: $file=~s-^/adm/wrapper/-/-; 6381: $file=~s-^/adm/coursedocs/showdoc/-/-; 6382: } 6383: if ($file=~m-^\Q$perlvar{'lonDocRoot'}\E-) { 6384: $file=~s-^\Q$perlvar{'lonDocRoot'}\E--; 6385: } elsif ($file=~m-/home/(\w+)/public_html/-) { 6386: $file=~s-^/home/(\w+)/public_html/-/~$1/-; 6387: } elsif ($file=~m-^\Q$perlvar{'lonUsersDir'}\E-) { 6388: $file=~s-^/home/httpd/lonUsers/([^/]*)/./././([^/]*)/userfiles/ 6389: -/uploaded/$1/$2/-x; 6390: } 6391: return $file; 6392: } 6393: 6394: sub current_machine_domains { 6395: my $hostname=$hostname{$perlvar{'lonHostID'}}; 6396: my @domains; 6397: while( my($id, $name) = each(%hostname)) { 6398: # &logthis("-$id-$name-$hostname-"); 6399: if ($hostname eq $name) { 6400: push(@domains,$hostdom{$id}); 6401: } 6402: } 6403: return @domains; 6404: } 6405: 6406: sub current_machine_ids { 6407: my $hostname=$hostname{$perlvar{'lonHostID'}}; 6408: my @ids; 6409: while( my($id, $name) = each(%hostname)) { 6410: # &logthis("-$id-$name-$hostname-"); 6411: if ($hostname eq $name) { 6412: push(@ids,$id); 6413: } 6414: } 6415: return @ids; 6416: } 6417: 6418: # ------------------------------------------------------------- Declutters URLs 6419: 6420: sub declutter { 6421: my $thisfn=shift; 6422: if ($thisfn=~m|^/enc/|) { $thisfn=&Apache::lonenc::unencrypted($thisfn); } 6423: $thisfn=~s/^\Q$perlvar{'lonDocRoot'}\E//; 6424: $thisfn=~s/^\///; 6425: $thisfn=~s|^adm/wrapper/||; 6426: $thisfn=~s|^adm/coursedocs/showdoc/||; 6427: $thisfn=~s/^res\///; 6428: $thisfn=~s/\?.+$//; 6429: return $thisfn; 6430: } 6431: 6432: # ------------------------------------------------------------- Clutter up URLs 6433: 6434: sub clutter { 6435: my $thisfn='/'.&declutter(shift); 6436: unless ($thisfn=~/^\/(uploaded|editupload|adm|userfiles|ext|raw|priv|public)\//) { 6437: $thisfn='/res'.$thisfn; 6438: } 6439: if ($thisfn !~m|/adm|) { 6440: if ($thisfn =~ m|/ext/|) { 6441: $thisfn='/adm/wrapper'.$thisfn; 6442: } else { 6443: my ($ext) = ($thisfn =~ /\.(\w+)$/); 6444: my $embstyle=&Apache::loncommon::fileembstyle($ext); 6445: if ($embstyle eq 'ssi' 6446: || ($embstyle eq 'hdn') 6447: || ($embstyle eq 'rat') 6448: || ($embstyle eq 'prv') 6449: || ($embstyle eq 'ign')) { 6450: #do nothing with these 6451: } elsif (($embstyle eq 'img') 6452: || ($embstyle eq 'emb') 6453: || ($embstyle eq 'wrp')) { 6454: $thisfn='/adm/wrapper'.$thisfn; 6455: } elsif ($embstyle eq 'unk' 6456: && $thisfn!~/\.(sequence|page)$/) { 6457: $thisfn='/adm/coursedocs/showdoc'.$thisfn; 6458: } else { 6459: # &logthis("Got a blank emb style"); 6460: } 6461: } 6462: } 6463: return $thisfn; 6464: } 6465: 6466: sub freeze_escape { 6467: my ($value)=@_; 6468: if (ref($value)) { 6469: $value=&nfreeze($value); 6470: return '__FROZEN__'.&escape($value); 6471: } 6472: return &escape($value); 6473: } 6474: 6475: # -------------------------------------------------------- Escape Special Chars 6476: 6477: sub escape { 6478: my $str=shift; 6479: $str =~ s/(\W)/"%".unpack('H2',$1)/eg; 6480: return $str; 6481: } 6482: 6483: # ----------------------------------------------------- Un-Escape Special Chars 6484: 6485: sub unescape { 6486: my $str=shift; 6487: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; 6488: return $str; 6489: } 6490: 6491: sub thaw_unescape { 6492: my ($value)=@_; 6493: if ($value =~ /^__FROZEN__/) { 6494: substr($value,0,10,undef); 6495: $value=&unescape($value); 6496: return &thaw($value); 6497: } 6498: return &unescape($value); 6499: } 6500: 6501: sub correct_line_ends { 6502: my ($result)=@_; 6503: $$result =~s/\r\n/\n/mg; 6504: $$result =~s/\r/\n/mg; 6505: } 6506: # ================================================================ Main Program 6507: 6508: sub goodbye { 6509: &logthis("Starting Shut down"); 6510: #not converted to using infrastruture and probably shouldn't be 6511: &logthis(sprintf("%-20s is %s",'%badServerCache',length(&freeze(\%badServerCache)))); 6512: #converted 6513: # &logthis(sprintf("%-20s is %s",'%metacache',scalar(%metacache))); 6514: &logthis(sprintf("%-20s is %s",'%homecache',length(&freeze(\%homecache)))); 6515: # &logthis(sprintf("%-20s is %s",'%titlecache',length(&freeze(\%titlecache)))); 6516: # &logthis(sprintf("%-20s is %s",'%courseresdatacache',length(&freeze(\%courseresdatacache)))); 6517: #1.1 only 6518: # &logthis(sprintf("%-20s is %s",'%userresdatacache',length(&freeze(\%userresdatacache)))); 6519: # &logthis(sprintf("%-20s is %s",'%getsectioncache',length(&freeze(\%getsectioncache)))); 6520: # &logthis(sprintf("%-20s is %s",'%courseresversioncache',length(&freeze(\%courseresversioncache)))); 6521: # &logthis(sprintf("%-20s is %s",'%resversioncache',length(&freeze(\%resversioncache)))); 6522: &logthis(sprintf("%-20s is %s",'%remembered',length(&freeze(\%remembered)))); 6523: &logthis(sprintf("%-20s is %s",'kicks',$kicks)); 6524: &logthis(sprintf("%-20s is %s",'hits',$hits)); 6525: &flushcourselogs(); 6526: &logthis("Shutting down"); 6527: return DONE; 6528: } 6529: 6530: BEGIN { 6531: # ----------------------------------- Read loncapa.conf and loncapa_apache.conf 6532: unless ($readit) { 6533: { 6534: # FIXME: Use LONCAPA::Configuration::read_conf here and omit next block 6535: open(my $config,"</etc/httpd/conf/loncapa.conf"); 6536: 6537: while (my $configline=<$config>) { 6538: if ($configline=~/\S/ && $configline =~ /^[^\#]*PerlSetVar/) { 6539: my ($dummy,$varname,$varvalue)=split(/\s+/,$configline); 6540: chomp($varvalue); 6541: $perlvar{$varname}=$varvalue; 6542: } 6543: } 6544: close($config); 6545: } 6546: { 6547: open(my $config,"</etc/httpd/conf/loncapa_apache.conf"); 6548: 6549: while (my $configline=<$config>) { 6550: if ($configline =~ /^[^\#]*PerlSetVar/) { 6551: my ($dummy,$varname,$varvalue)=split(/\s+/,$configline); 6552: chomp($varvalue); 6553: $perlvar{$varname}=$varvalue; 6554: } 6555: } 6556: close($config); 6557: } 6558: 6559: # ------------------------------------------------------------ Read domain file 6560: { 6561: %domaindescription = (); 6562: %domain_auth_def = (); 6563: %domain_auth_arg_def = (); 6564: my $fh; 6565: if (open($fh,"<".$Apache::lonnet::perlvar{'lonTabDir'}.'/domain.tab')) { 6566: while (<$fh>) { 6567: next if (/^(\#|\s*$)/); 6568: # next if /^\#/; 6569: chomp; 6570: my ($domain, $domain_description, $def_auth, $def_auth_arg, 6571: $def_lang, $city, $longi, $lati, $primary) = split(/:/,$_); 6572: $domain_auth_def{$domain}=$def_auth; 6573: $domain_auth_arg_def{$domain}=$def_auth_arg; 6574: $domaindescription{$domain}=$domain_description; 6575: $domain_lang_def{$domain}=$def_lang; 6576: $domain_city{$domain}=$city; 6577: $domain_longi{$domain}=$longi; 6578: $domain_lati{$domain}=$lati; 6579: $domain_primary{$domain}=$primary; 6580: 6581: # &logthis("Domain.tab: $domain, $domain_auth_def{$domain}, $domain_auth_arg_def{$domain},$domaindescription{$domain}"); 6582: # &logthis("Domain.tab: $domain ".$domaindescription{$domain} ); 6583: } 6584: } 6585: close ($fh); 6586: } 6587: 6588: 6589: # ------------------------------------------------------------- Read hosts file 6590: { 6591: open(my $config,"<$perlvar{'lonTabDir'}/hosts.tab"); 6592: 6593: while (my $configline=<$config>) { 6594: next if ($configline =~ /^(\#|\s*$)/); 6595: chomp($configline); 6596: my ($id,$domain,$role,$name)=split(/:/,$configline); 6597: $name=~s/\s//g; 6598: if ($id && $domain && $role && $name) { 6599: $hostname{$id}=$name; 6600: $hostdom{$id}=$domain; 6601: if ($role eq 'library') { $libserv{$id}=$name; } 6602: } 6603: } 6604: close($config); 6605: # FIXME: dev server don't want this, production servers _do_ want this 6606: #&get_iphost(); 6607: } 6608: 6609: sub get_iphost { 6610: if (%iphost) { return %iphost; } 6611: my %name_to_ip; 6612: foreach my $id (keys(%hostname)) { 6613: my $name=$hostname{$id}; 6614: my $ip; 6615: if (!exists($name_to_ip{$name})) { 6616: $ip = gethostbyname($name); 6617: if (!$ip || length($ip) ne 4) { 6618: &logthis("Skipping host $id name $name no IP found\n"); 6619: next; 6620: } 6621: $ip=inet_ntoa($ip); 6622: $name_to_ip{$name} = $ip; 6623: } else { 6624: $ip = $name_to_ip{$name}; 6625: } 6626: push(@{$iphost{$ip}},$id); 6627: } 6628: return %iphost; 6629: } 6630: 6631: # ------------------------------------------------------ Read spare server file 6632: { 6633: open(my $config,"<$perlvar{'lonTabDir'}/spare.tab"); 6634: 6635: while (my $configline=<$config>) { 6636: chomp($configline); 6637: if ($configline) { 6638: $spareid{$configline}=1; 6639: } 6640: } 6641: close($config); 6642: } 6643: # ------------------------------------------------------------ Read permissions 6644: { 6645: open(my $config,"<$perlvar{'lonTabDir'}/roles.tab"); 6646: 6647: while (my $configline=<$config>) { 6648: chomp($configline); 6649: if ($configline) { 6650: my ($role,$perm)=split(/ /,$configline); 6651: if ($perm ne '') { $pr{$role}=$perm; } 6652: } 6653: } 6654: close($config); 6655: } 6656: 6657: # -------------------------------------------- Read plain texts for permissions 6658: { 6659: open(my $config,"<$perlvar{'lonTabDir'}/rolesplain.tab"); 6660: 6661: while (my $configline=<$config>) { 6662: chomp($configline); 6663: if ($configline) { 6664: my ($short,$plain)=split(/:/,$configline); 6665: if ($plain ne '') { $prp{$short}=$plain; } 6666: } 6667: } 6668: close($config); 6669: } 6670: 6671: # ---------------------------------------------------------- Read package table 6672: { 6673: open(my $config,"<$perlvar{'lonTabDir'}/packages.tab"); 6674: 6675: while (my $configline=<$config>) { 6676: if ($configline !~ /\S/ || $configline=~/^#/) { next; } 6677: chomp($configline); 6678: my ($short,$plain)=split(/:/,$configline); 6679: my ($pack,$name)=split(/\&/,$short); 6680: if ($plain ne '') { 6681: $packagetab{$pack.'&'.$name.'&name'}=$name; 6682: $packagetab{$short}=$plain; 6683: } 6684: } 6685: close($config); 6686: } 6687: 6688: # ------------- set up temporary directory 6689: { 6690: $tmpdir = $perlvar{'lonDaemons'}.'/tmp/'; 6691: 6692: } 6693: 6694: $memcache=new Cache::Memcached({'servers'=>['127.0.0.1:11211']}); 6695: 6696: $processmarker='_'.time.'_'.$perlvar{'lonHostID'}; 6697: $dumpcount=0; 6698: 6699: &logtouch(); 6700: &logthis('<font color="yellow">INFO: Read configuration</font>'); 6701: $readit=1; 6702: { 6703: use integer; 6704: my $test=(2**32)+1; 6705: if ($test != 0) { $_64bit=1; } else { $_64bit=0; } 6706: &logthis(" Detected 64bit platform ($_64bit)"); 6707: } 6708: } 6709: } 6710: 6711: 1; 6712: __END__ 6713: 6714: =pod 6715: 6716: =head1 NAME 6717: 6718: Apache::lonnet - Subroutines to ask questions about things in the network. 6719: 6720: =head1 SYNOPSIS 6721: 6722: Invoked by other LON-CAPA modules, when they need to talk to or about objects in the network. 6723: 6724: &Apache::lonnet::SUBROUTINENAME(ARGUMENTS); 6725: 6726: Common parameters: 6727: 6728: =over 4 6729: 6730: =item * 6731: 6732: $uname : an internal username (if $cname expecting a course Id specifically) 6733: 6734: =item * 6735: 6736: $udom : a domain (if $cdom expecting a course's domain specifically) 6737: 6738: =item * 6739: 6740: $symb : a resource instance identifier 6741: 6742: =item * 6743: 6744: $namespace : the name of a .db file that contains the data needed or 6745: being set. 6746: 6747: =back 6748: 6749: =head1 OVERVIEW 6750: 6751: lonnet provides subroutines which interact with the 6752: lonc/lond (TCP) network layer of LON-CAPA. They can be used to ask 6753: about classes, users, and resources. 6754: 6755: For many of these objects you can also use this to store data about 6756: them or modify them in various ways. 6757: 6758: =head2 Symbs 6759: 6760: To identify a specific instance of a resource, LON-CAPA uses symbols 6761: or "symbs"X<symb>. These identifiers are built from the URL of the 6762: map, the resource number of the resource in the map, and the URL of 6763: the resource itself. The latter is somewhat redundant, but might help 6764: if maps change. 6765: 6766: An example is 6767: 6768: msu/korte/parts/part1.sequence___19___msu/korte/tests/part12.problem 6769: 6770: The respective map entry is 6771: 6772: <resource id="19" src="/res/msu/korte/tests/part12.problem" 6773: title="Problem 2"> 6774: </resource> 6775: 6776: Symbs are used by the random number generator, as well as to store and 6777: restore data specific to a certain instance of for example a problem. 6778: 6779: =head2 Storing And Retrieving Data 6780: 6781: X<store()>X<cstore()>X<restore()>Three of the most important functions 6782: in C<lonnet.pm> are C<&Apache::lonnet::cstore()>, 6783: C<&Apache::lonnet:restore()>, and C<&Apache::lonnet::store()>, which 6784: is is the non-critical message twin of cstore. These functions are for 6785: handlers to store a perl hash to a user's permanent data space in an 6786: easy manner, and to retrieve it again on another call. It is expected 6787: that a handler would use this once at the beginning to retrieve data, 6788: and then again once at the end to send only the new data back. 6789: 6790: The data is stored in the user's data directory on the user's 6791: homeserver under the ID of the course. 6792: 6793: The hash that is returned by restore will have all of the previous 6794: value for all of the elements of the hash. 6795: 6796: Example: 6797: 6798: #creating a hash 6799: my %hash; 6800: $hash{'foo'}='bar'; 6801: 6802: #storing it 6803: &Apache::lonnet::cstore(\%hash); 6804: 6805: #changing a value 6806: $hash{'foo'}='notbar'; 6807: 6808: #adding a new value 6809: $hash{'bar'}='foo'; 6810: &Apache::lonnet::cstore(\%hash); 6811: 6812: #retrieving the hash 6813: my %history=&Apache::lonnet::restore(); 6814: 6815: #print the hash 6816: foreach my $key (sort(keys(%history))) { 6817: print("\%history{$key} = $history{$key}"); 6818: } 6819: 6820: Will print out: 6821: 6822: %history{1:foo} = bar 6823: %history{1:keys} = foo:timestamp 6824: %history{1:timestamp} = 990455579 6825: %history{2:bar} = foo 6826: %history{2:foo} = notbar 6827: %history{2:keys} = foo:bar:timestamp 6828: %history{2:timestamp} = 990455580 6829: %history{bar} = foo 6830: %history{foo} = notbar 6831: %history{timestamp} = 990455580 6832: %history{version} = 2 6833: 6834: Note that the special hash entries C<keys>, C<version> and 6835: C<timestamp> were added to the hash. C<version> will be equal to the 6836: total number of versions of the data that have been stored. The 6837: C<timestamp> attribute will be the UNIX time the hash was 6838: stored. C<keys> is available in every historical section to list which 6839: keys were added or changed at a specific historical revision of a 6840: hash. 6841: 6842: B<Warning>: do not store the hash that restore returns directly. This 6843: will cause a mess since it will restore the historical keys as if the 6844: were new keys. I.E. 1:foo will become 1:1:foo etc. 6845: 6846: Calling convention: 6847: 6848: my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$uname,$home); 6849: &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$uname,$home); 6850: 6851: For more detailed information, see lonnet specific documentation. 6852: 6853: =head1 RETURN MESSAGES 6854: 6855: =over 4 6856: 6857: =item * B<con_lost>: unable to contact remote host 6858: 6859: =item * B<con_delayed>: unable to contact remote host, message will be delivered 6860: when the connection is brought back up 6861: 6862: =item * B<con_failed>: unable to contact remote host and unable to save message 6863: for later delivery 6864: 6865: =item * B<error:>: an error a occured, a description of the error follows the : 6866: 6867: =item * B<no_such_host>: unable to fund a host associated with the user/domain 6868: that was requested 6869: 6870: =back 6871: 6872: =head1 PUBLIC SUBROUTINES 6873: 6874: =head2 Session Environment Functions 6875: 6876: =over 4 6877: 6878: =item * 6879: X<appenv()> 6880: B<appenv(%hash)>: the value of %hash is written to 6881: the user envirnoment file, and will be restored for each access this 6882: user makes during this session, also modifies the %env for the current 6883: process 6884: 6885: =item * 6886: X<delenv()> 6887: B<delenv($regexp)>: removes all items from the session 6888: environment file that matches the regular expression in $regexp. The 6889: values are also delted from the current processes %env. 6890: 6891: =back 6892: 6893: =head2 User Information 6894: 6895: =over 4 6896: 6897: =item * 6898: X<queryauthenticate()> 6899: B<queryauthenticate($uname,$udom)>: try to determine user's current 6900: authentication scheme 6901: 6902: =item * 6903: X<authenticate()> 6904: B<authenticate($uname,$upass,$udom)>: try to 6905: authenticate user from domain's lib servers (first use the current 6906: one). C<$upass> should be the users password. 6907: 6908: =item * 6909: X<homeserver()> 6910: B<homeserver($uname,$udom)>: find the server which has 6911: the user's directory and files (there must be only one), this caches 6912: the answer, and also caches if there is a borken connection. 6913: 6914: =item * 6915: X<idget()> 6916: B<idget($udom,@ids)>: find the usernames behind a list of IDs 6917: (IDs are a unique resource in a domain, there must be only 1 ID per 6918: username, and only 1 username per ID in a specific domain) (returns 6919: hash: id=>name,id=>name) 6920: 6921: =item * 6922: X<idrget()> 6923: B<idrget($udom,@unames)>: find the IDs behind a list of 6924: usernames (returns hash: name=>id,name=>id) 6925: 6926: =item * 6927: X<idput()> 6928: B<idput($udom,%ids)>: store away a list of names and associated IDs 6929: 6930: =item * 6931: X<rolesinit()> 6932: B<rolesinit($udom,$username,$authhost)>: get user privileges 6933: 6934: =item * 6935: X<getsection()> 6936: B<getsection($udom,$uname,$cname)>: finds the section of student in the 6937: course $cname, return section name/number or '' for "not in course" 6938: and '-1' for "no section" 6939: 6940: =item * 6941: X<userenvironment()> 6942: B<userenvironment($udom,$uname,@what)>: gets the values of the keys 6943: passed in @what from the requested user's environment, returns a hash 6944: 6945: =back 6946: 6947: =head2 User Roles 6948: 6949: =over 4 6950: 6951: =item * 6952: 6953: allowed($priv,$uri) : check for a user privilege; returns codes for allowed 6954: actions 6955: F: full access 6956: U,I,K: authentication modes (cxx only) 6957: '': forbidden 6958: 1: user needs to choose course 6959: 2: browse allowed 6960: 6961: =item * 6962: 6963: definerole($rolename,$sysrole,$domrole,$courole) : define role; define a custom 6964: role rolename set privileges in format of lonTabs/roles.tab for system, domain, 6965: and course level 6966: 6967: =item * 6968: 6969: plaintext($short) : return value in %prp hash (rolesplain.tab); plain text 6970: explanation of a user role term 6971: 6972: =back 6973: 6974: =head2 User Modification 6975: 6976: =over 4 6977: 6978: =item * 6979: 6980: assignrole($udom,$uname,$url,$role,$end,$start) : assign role; give a role to a 6981: user for the level given by URL. Optional start and end dates (leave empty 6982: string or zero for "no date") 6983: 6984: =item * 6985: 6986: changepass($uname,$udom,$currentpass,$newpass,$server) : attempts to 6987: change a users, password, possible return values are: ok, 6988: pwchange_failure, non_authorized, auth_mode_error, unknown_user, 6989: refused 6990: 6991: =item * 6992: 6993: modifyuserauth($udom,$uname,$umode,$upass) : modify user authentication 6994: 6995: =item * 6996: 6997: modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene) : 6998: modify user 6999: 7000: =item * 7001: 7002: modifystudent 7003: 7004: modify a students enrollment and identification information. 7005: The course id is resolved based on the current users environment. 7006: This means the envoking user must be a course coordinator or otherwise 7007: associated with a course. 7008: 7009: This call is essentially a wrapper for lonnet::modifyuser and 7010: lonnet::modify_student_enrollment 7011: 7012: Inputs: 7013: 7014: =over 4 7015: 7016: =item B<$udom> Students loncapa domain 7017: 7018: =item B<$uname> Students loncapa login name 7019: 7020: =item B<$uid> Students id/student number 7021: 7022: =item B<$umode> Students authentication mode 7023: 7024: =item B<$upass> Students password 7025: 7026: =item B<$first> Students first name 7027: 7028: =item B<$middle> Students middle name 7029: 7030: =item B<$last> Students last name 7031: 7032: =item B<$gene> Students generation 7033: 7034: =item B<$usec> Students section in course 7035: 7036: =item B<$end> Unix time of the roles expiration 7037: 7038: =item B<$start> Unix time of the roles start date 7039: 7040: =item B<$forceid> If defined, allow $uid to be changed 7041: 7042: =item B<$desiredhome> server to use as home server for student 7043: 7044: =back 7045: 7046: =item * 7047: 7048: modify_student_enrollment 7049: 7050: Change a students enrollment status in a class. The environment variable 7051: 'role.request.course' must be defined for this function to proceed. 7052: 7053: Inputs: 7054: 7055: =over 4 7056: 7057: =item $udom, students domain 7058: 7059: =item $uname, students name 7060: 7061: =item $uid, students user id 7062: 7063: =item $first, students first name 7064: 7065: =item $middle 7066: 7067: =item $last 7068: 7069: =item $gene 7070: 7071: =item $usec 7072: 7073: =item $end 7074: 7075: =item $start 7076: 7077: =back 7078: 7079: 7080: =item * 7081: 7082: assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start) : assign 7083: custom role; give a custom role to a user for the level given by URL. Specify 7084: name and domain of role author, and role name 7085: 7086: =item * 7087: 7088: revokerole($udom,$uname,$url,$role) : revoke a role for url 7089: 7090: =item * 7091: 7092: revokecustomrole($udom,$uname,$url,$role) : revoke a custom role 7093: 7094: =back 7095: 7096: =head2 Course Infomation 7097: 7098: =over 4 7099: 7100: =item * 7101: 7102: coursedescription($courseid) : returns a hash of information about the 7103: specified course id, including all environment settings for the 7104: course, the description of the course will be in the hash under the 7105: key 'description' 7106: 7107: =item * 7108: 7109: resdata($name,$domain,$type,@which) : request for current parameter 7110: setting for a specific $type, where $type is either 'course' or 'user', 7111: @what should be a list of parameters to ask about. This routine caches 7112: answers for 5 minutes. 7113: 7114: =back 7115: 7116: =head2 Course Modification 7117: 7118: =over 4 7119: 7120: =item * 7121: 7122: writecoursepref($courseid,%prefs) : write preferences (environment 7123: database) for a course 7124: 7125: =item * 7126: 7127: createcourse($udom,$description,$url) : make/modify course 7128: 7129: =back 7130: 7131: =head2 Resource Subroutines 7132: 7133: =over 4 7134: 7135: =item * 7136: 7137: subscribe($fname) : subscribe to a resource, returns URL if possible (probably should use repcopy instead) 7138: 7139: =item * 7140: 7141: repcopy($filename) : subscribes to the requested file, and attempts to 7142: replicate from the owning library server, Might return 7143: 'unavailable', 'not_found', 'forbidden', 'ok', or 7144: 'bad_request', also attempts to grab the metadata for the 7145: resource. Expects the local filesystem pathname 7146: (/home/httpd/html/res/....) 7147: 7148: =back 7149: 7150: =head2 Resource Information 7151: 7152: =over 4 7153: 7154: =item * 7155: 7156: EXT($varname,$symb,$udom,$uname) : evaluates and returns the value of 7157: a vairety of different possible values, $varname should be a request 7158: string, and the other parameters can be used to specify who and what 7159: one is asking about. 7160: 7161: Possible values for $varname are environment.lastname (or other item 7162: from the envirnment hash), user.name (or someother aspect about the 7163: user), resource.0.maxtries (or some other part and parameter of a 7164: resource) 7165: 7166: =item * 7167: 7168: directcondval($number) : get current value of a condition; reads from a state 7169: string 7170: 7171: =item * 7172: 7173: condval($condidx) : value of condition index based on state 7174: 7175: =item * 7176: 7177: metadata($uri,$what,$liburi,$prefix,$depthcount) : request a 7178: resource's metadata, $what should be either a specific key, or either 7179: 'keys' (to get a list of possible keys) or 'packages' to get a list of 7180: packages that this resource currently uses, the last 3 arguments are only used internally for recursive metadata. 7181: 7182: this function automatically caches all requests 7183: 7184: =item * 7185: 7186: metadata_query($query,$custom,$customshow) : make a metadata query against the 7187: network of library servers; returns file handle of where SQL and regex results 7188: will be stored for query 7189: 7190: =item * 7191: 7192: symbread($filename) : return symbolic list entry (filename argument optional); 7193: returns the data handle 7194: 7195: =item * 7196: 7197: symbverify($symb,$thisfn) : verifies that $symb actually exists and is 7198: a possible symb for the URL in $thisfn, and if is an encryypted 7199: resource that the user accessed using /enc/ returns a 1 on success, 0 7200: on failure, user must be in a course, as it assumes the existance of 7201: the course initial hash, and uses $env('request.course.id'} 7202: 7203: 7204: =item * 7205: 7206: symbclean($symb) : removes versions numbers from a symb, returns the 7207: cleaned symb 7208: 7209: =item * 7210: 7211: is_on_map($uri) : checks if the $uri is somewhere on the current 7212: course map, user must be in a course for it to work. 7213: 7214: =item * 7215: 7216: numval($salt) : return random seed value (addend for rndseed) 7217: 7218: =item * 7219: 7220: rndseed($symb,$courseid,$udom,$uname) : create a random sum; returns 7221: a random seed, all arguments are optional, if they aren't sent it uses the 7222: environment to derive them. Note: if symb isn't sent and it can't get one 7223: from &symbread it will use the current time as its return value 7224: 7225: =item * 7226: 7227: ireceipt($funame,$fudom,$fucourseid,$fusymb) : return unique, 7228: unfakeable, receipt 7229: 7230: =item * 7231: 7232: receipt() : API to ireceipt working off of env values; given out to users 7233: 7234: =item * 7235: 7236: countacc($url) : count the number of accesses to a given URL 7237: 7238: =item * 7239: 7240: checkout($symb,$tuname,$tudom,$tcrsid) : creates a record of a user having looked at an item, most likely printed out or otherwise using a resource 7241: 7242: =item * 7243: 7244: checkin($token) : updates that a resource has beeen returned (a hard copy version for instance) and returns the data that $token was Checkout with ($symb, $tuname, $tudom, and $tcrsid) 7245: 7246: =item * 7247: 7248: expirespread($uname,$udom,$stype,$usymb) : set expire date for spreadsheet 7249: 7250: =item * 7251: 7252: devalidate($symb) : devalidate temporary spreadsheet calculations, 7253: forcing spreadsheet to reevaluate the resource scores next time. 7254: 7255: =back 7256: 7257: =head2 Storing/Retreiving Data 7258: 7259: =over 4 7260: 7261: =item * 7262: 7263: store($storehash,$symb,$namespace,$udom,$uname) : stores hash permanently 7264: for this url; hashref needs to be given and should be a \%hashname; the 7265: remaining args aren't required and if they aren't passed or are '' they will 7266: be derived from the env 7267: 7268: =item * 7269: 7270: cstore($storehash,$symb,$namespace,$udom,$uname) : same as store but 7271: uses critical subroutine 7272: 7273: =item * 7274: 7275: restore($symb,$namespace,$udom,$uname) : returns hash for this symb; 7276: all args are optional 7277: 7278: =item * 7279: 7280: dumpstore($namespace,$udom,$uname,$regexp,$range) : 7281: dumps the complete (or key matching regexp) namespace into a hash 7282: ($udom, $uname, $regexp, $range are optional) for a namespace that is 7283: normally &store()ed into 7284: 7285: $range should be either an integer '100' (give me the first 100 7286: matching records) 7287: or be two integers sperated by a - with no spaces 7288: '30-50' (give me the 30th through the 50th matching 7289: records) 7290: 7291: 7292: =item * 7293: 7294: putstore($namespace,$symb,$version,$storehash,$udomain,$uname) : 7295: replaces a &store() version of data with a replacement set of data 7296: for a particular resource in a namespace passed in the $storehash hash 7297: reference 7298: 7299: =item * 7300: 7301: tmpstore($storehash,$symb,$namespace,$udom,$uname) : storage that 7302: works very similar to store/cstore, but all data is stored in a 7303: temporary location and can be reset using tmpreset, $storehash should 7304: be a hash reference, returns nothing on success 7305: 7306: =item * 7307: 7308: tmprestore($symb,$namespace,$udom,$uname) : storage that works very 7309: similar to restore, but all data is stored in a temporary location and 7310: can be reset using tmpreset. Returns a hash of values on success, 7311: error string otherwise. 7312: 7313: =item * 7314: 7315: tmpreset($symb,$namespace,$udom,$uname) : temporary storage reset, 7316: deltes all keys for $symb form the temporary storage hash. 7317: 7318: =item * 7319: 7320: get($namespace,$storearr,$udom,$uname) : returns hash with keys from array 7321: reference filled in from namesp ($udom and $uname are optional) 7322: 7323: =item * 7324: 7325: del($namespace,$storearr,$udom,$uname) : deletes keys out of array from 7326: namesp ($udom and $uname are optional) 7327: 7328: =item * 7329: 7330: dump($namespace,$udom,$uname,$regexp,$range) : 7331: dumps the complete (or key matching regexp) namespace into a hash 7332: ($udom, $uname, $regexp, $range are optional) 7333: 7334: $range should be either an integer '100' (give me the first 100 7335: matching records) 7336: or be two integers sperated by a - with no spaces 7337: '30-50' (give me the 30th through the 50th matching 7338: records) 7339: =item * 7340: 7341: inc($namespace,$store,$udom,$uname) : increments $store in $namespace. 7342: $store can be a scalar, an array reference, or if the amount to be 7343: incremented is > 1, a hash reference. 7344: 7345: ($udom and $uname are optional) 7346: 7347: =item * 7348: 7349: put($namespace,$storehash,$udom,$uname) : stores hash in namesp 7350: ($udom and $uname are optional) 7351: 7352: =item * 7353: 7354: cput($namespace,$storehash,$udom,$uname) : critical put 7355: ($udom and $uname are optional) 7356: 7357: =item * 7358: 7359: eget($namespace,$storearr,$udom,$uname) : returns hash with keys from array 7360: reference filled in from namesp (encrypts the return communication) 7361: ($udom and $uname are optional) 7362: 7363: =item * 7364: 7365: log($udom,$name,$home,$message) : write to permanent log for user; use 7366: critical subroutine 7367: 7368: =back 7369: 7370: =head2 Network Status Functions 7371: 7372: =over 4 7373: 7374: =item * 7375: 7376: dirlist($uri) : return directory list based on URI 7377: 7378: =item * 7379: 7380: spareserver() : find server with least workload from spare.tab 7381: 7382: =back 7383: 7384: =head2 Apache Request 7385: 7386: =over 4 7387: 7388: =item * 7389: 7390: ssi($url,%hash) : server side include, does a complete request cycle on url to 7391: localhost, posts hash 7392: 7393: =back 7394: 7395: =head2 Data to String to Data 7396: 7397: =over 4 7398: 7399: =item * 7400: 7401: hash2str(%hash) : convert a hash into a string complete with escaping and '=' 7402: and '&' separators, supports elements that are arrayrefs and hashrefs 7403: 7404: =item * 7405: 7406: hashref2str($hashref) : convert a hashref into a string complete with 7407: escaping and '=' and '&' separators, supports elements that are 7408: arrayrefs and hashrefs 7409: 7410: =item * 7411: 7412: arrayref2str($arrayref) : convert an arrayref into a string complete 7413: with escaping and '&' separators, supports elements that are arrayrefs 7414: and hashrefs 7415: 7416: =item * 7417: 7418: str2hash($string) : convert string to hash using unescaping and 7419: splitting on '=' and '&', supports elements that are arrayrefs and 7420: hashrefs 7421: 7422: =item * 7423: 7424: str2array($string) : convert string to hash using unescaping and 7425: splitting on '&', supports elements that are arrayrefs and hashrefs 7426: 7427: =back 7428: 7429: =head2 Logging Routines 7430: 7431: =over 4 7432: 7433: These routines allow one to make log messages in the lonnet.log and 7434: lonnet.perm logfiles. 7435: 7436: =item * 7437: 7438: logtouch() : make sure the logfile, lonnet.log, exists 7439: 7440: =item * 7441: 7442: logthis() : append message to the normal lonnet.log file, it gets 7443: preiodically rolled over and deleted. 7444: 7445: =item * 7446: 7447: logperm() : append a permanent message to lonnet.perm.log, this log 7448: file never gets deleted by any automated portion of the system, only 7449: messages of critical importance should go in here. 7450: 7451: =back 7452: 7453: =head2 General File Helper Routines 7454: 7455: =over 4 7456: 7457: =item * 7458: 7459: getfile($file,$caller) : two cases - requests for files in /res or in /uploaded. 7460: (a) files in /uploaded 7461: (i) If a local copy of the file exists - 7462: compares modification date of local copy with last-modified date for 7463: definitive version stored on home server for course. If local copy is 7464: stale, requests a new version from the home server and stores it. 7465: If the original has been removed from the home server, then local copy 7466: is unlinked. 7467: (ii) If local copy does not exist - 7468: requests the file from the home server and stores it. 7469: 7470: If $caller is 'uploadrep': 7471: This indicates a call from lonuploadrep.pm (PerlHeaderParserHandler phase) 7472: for request for files originally uploaded via DOCS. 7473: - returns 'ok' if fresh local copy now available, -1 otherwise. 7474: 7475: Otherwise: 7476: This indicates a call from the content generation phase of the request. 7477: - returns the entire contents of the file or -1. 7478: 7479: (b) files in /res 7480: - returns the entire contents of a file or -1; 7481: it properly subscribes to and replicates the file if neccessary. 7482: 7483: 7484: =item * 7485: 7486: stat_file($url) : $url is expected to be a /res/ or /uploaded/ style file 7487: reference 7488: 7489: returns either a stat() list of data about the file or an empty list 7490: if the file doesn't exist or couldn't find out about it (connection 7491: problems or user unknown) 7492: 7493: =item * 7494: 7495: filelocation($dir,$file) : returns file system location of a file 7496: based on URI; meant to be "fairly clean" absolute reference, $dir is a 7497: directory that relative $file lookups are to looked in ($dir of /a/dir 7498: and a file of ../bob will become /a/bob) 7499: 7500: =item * 7501: 7502: hreflocation($dir,$file) : returns file system location or a URL; same as 7503: filelocation except for hrefs 7504: 7505: =item * 7506: 7507: declutter() : declutters URLs (remove docroot, beginning slashes, 'res' etc) 7508: 7509: =back 7510: 7511: =head2 Usererfile file routines (/uploaded*) 7512: 7513: =over 4 7514: 7515: =item * 7516: 7517: userfileupload(): main rotine for putting a file in a user or course's 7518: filespace, arguments are, 7519: 7520: formname - required - this is the name of the element in $env where the 7521: filename, and the contents of the file to create/modifed exist 7522: the filename is in $env{'form.'.$formname.'.filename'} and the 7523: contents of the file is located in $env{'form.'.$formname} 7524: coursedoc - if true, store the file in the course of the active role 7525: of the current user 7526: subdir - required - subdirectory to put the file in under ../userfiles/ 7527: if undefined, it will be placed in "unknown" 7528: 7529: (This routine calls clean_filename() to remove any dangerous 7530: characters from the filename, and then calls finuserfileupload() to 7531: complete the transaction) 7532: 7533: returns either the url of the uploaded file (/uploaded/....) if successful 7534: and /adm/notfound.html if unsuccessful 7535: 7536: =item * 7537: 7538: clean_filename(): routine for cleaing a filename up for storage in 7539: userfile space, argument is: 7540: 7541: filename - proposed filename 7542: 7543: returns: the new clean filename 7544: 7545: =item * 7546: 7547: finishuserfileupload(): routine that creaes and sends the file to 7548: userspace, probably shouldn't be called directly 7549: 7550: docuname: username or courseid of destination for the file 7551: docudom: domain of user/course of destination for the file 7552: formname: same as for userfileupload() 7553: fname: filename (inculding subdirectories) for the file 7554: 7555: returns either the url of the uploaded file (/uploaded/....) if successful 7556: and /adm/notfound.html if unsuccessful 7557: 7558: =item * 7559: 7560: renameuserfile(): renames an existing userfile to a new name 7561: 7562: Args: 7563: docuname: username or courseid of destination for the file 7564: docudom: domain of user/course of destination for the file 7565: old: current file name (including any subdirs under userfiles) 7566: new: desired file name (including any subdirs under userfiles) 7567: 7568: =item * 7569: 7570: mkdiruserfile(): creates a directory is a userfiles dir 7571: 7572: Args: 7573: docuname: username or courseid of destination for the file 7574: docudom: domain of user/course of destination for the file 7575: dir: dir to create (including any subdirs under userfiles) 7576: 7577: =item * 7578: 7579: removeuserfile(): removes a file that exists in userfiles 7580: 7581: Args: 7582: docuname: username or courseid of destination for the file 7583: docudom: domain of user/course of destination for the file 7584: fname: filname to delete (including any subdirs under userfiles) 7585: 7586: =item * 7587: 7588: removeuploadedurl(): convience function for removeuserfile() 7589: 7590: Args: 7591: url: a full /uploaded/... url to delete 7592: 7593: =back 7594: 7595: =head2 HTTP Helper Routines 7596: 7597: =over 4 7598: 7599: =item * 7600: 7601: escape() : unpack non-word characters into CGI-compatible hex codes 7602: 7603: =item * 7604: 7605: unescape() : pack CGI-compatible hex codes into actual non-word ASCII character 7606: 7607: =back 7608: 7609: =head1 PRIVATE SUBROUTINES 7610: 7611: =head2 Underlying communication routines (Shouldn't call) 7612: 7613: =over 4 7614: 7615: =item * 7616: 7617: subreply() : tries to pass a message to lonc, returns con_lost if incapable 7618: 7619: =item * 7620: 7621: reply() : uses subreply to send a message to remote machine, logs all failures 7622: 7623: =item * 7624: 7625: critical() : passes a critical message to another server; if cannot 7626: get through then place message in connection buffer directory and 7627: returns con_delayed, if incapable of saving message, returns 7628: con_failed 7629: 7630: =item * 7631: 7632: reconlonc() : tries to reconnect lonc client processes. 7633: 7634: =back 7635: 7636: =head2 Resource Access Logging 7637: 7638: =over 4 7639: 7640: =item * 7641: 7642: flushcourselogs() : flush (save) buffer logs and access logs 7643: 7644: =item * 7645: 7646: courselog($what) : save message for course in hash 7647: 7648: =item * 7649: 7650: courseacclog($what) : save message for course using &courselog(). Perform 7651: special processing for specific resource types (problems, exams, quizzes, etc). 7652: 7653: =item * 7654: 7655: goodbye() : flush course logs and log shutting down; it is called in srm.conf 7656: as a PerlChildExitHandler 7657: 7658: =back 7659: 7660: =head2 Other 7661: 7662: =over 4 7663: 7664: =item * 7665: 7666: symblist($mapname,%newhash) : update symbolic storage links 7667: 7668: =back 7669: 7670: =cut