1: # The LearningOnline Network
2: # TCP networking package
3: #
4: # Functions for use by content handlers:
5: #
6: # metadata_query(sql-query-string,custom-metadata-regex) :
7: # returns file handle of where sql and
8: # regex results will be stored for query
9: # plaintext(short) : plain text explanation of short term
10: # fileembstyle(ext) : embed style in page for file extension
11: # filedescription(ext) : descriptor text for file extension
12: # allowed(short,url) : returns codes for allowed actions
13: # F: full access
14: # U,I,K: authentication modes (cxx only)
15: # '': forbidden
16: # 1: user needs to choose course
17: # 2: browse allowed
18: # definerole(rolename,sys,dom,cou) : define a custom role rolename
19: # set privileges in format of lonTabs/roles.tab for
20: # system, domain and course level,
21: # assignrole(udom,uname,url,role,end,start) : give a role to a user for the
22: # level given by url. Optional start and end dates
23: # (leave empty string or zero for "no date")
24: # assigncustomrole (udom,uname,url,rdom,rnam,rolename,end,start) : give a
25: # custom role to a user for the level given by url.
26: # Specify name and domain of role author, and role name
27: # revokerole (udom,uname,url,role) : Revoke a role for url
28: # revokecustomrole (udom,uname,url,rdom,rnam,rolename) : Revoke a custom role
29: # appenv(hash) : adds hash to session environment
30: # delenv(varname) : deletes all environment entries starting with varname
31: # store(hashref,symb,courseid,udom,uname)
32: # : stores hash permanently for this url
33: # hashref needs to be given, and should be a \%hashname
34: # the remaining args aren't required and if they aren't
35: # passed or are '' they will be derived from the ENV
36: # cstore(hashref,symb,courseid,udom,uname)
37: # : same as store but uses the critical interface to
38: # guarentee a store
39: # restore(symb,courseid,udom,uname)
40: # : returns hash for this symb, all args are optional
41: # if they aren't given they will be derived from the
42: # current enviroment
43: # eget(namesp,array) : returns hash with keys from array filled in from namesp
44: # get(namesp,arrayref,udom,uname)
45: # : returns hash with keys from array reference filled
46: # in from namesp
47: # if supplied uses udom as the domain and uname
48: # as the username for the dump (supply a courseid
49: # for the uname if you want a course database)
50: # if not supplied it uses %ENV to get the values
51: # del(namesp,array) : deletes keys out of array from namesp
52: # put(namesp,hash) : stores hash in namesp
53: # cput(namesp,hash) : critical put
54: # dump(namesp,udom,uname) : dumps the complete namespace into a hash
55: # if supplied uses udom as the domain and uname
56: # as the username for the dump (supply a courseid
57: # for the uname if you want a course database)
58: # if not supplied it uses %ENV to get the values
59: # ssi(url,hash) : does a complete request cycle on url to localhost, posts
60: # hash
61: # coursedescription(id) : returns and caches course description for id
62: # repcopy(filename) : replicate file
63: # dirlist(url) : gets a directory listing
64: # directcondval(index) : reading condition value of single condition from
65: # state string
66: # condval(index) : value of condition index based on state
67: # EXT(name) : value of a variable
68: # symblist(map,hash) : Updates symbolic storage links
69: # symbread([filename]) : returns the data handle (filename optional)
70: # rndseed() : returns a random seed
71: # receipt() : returns a receipt to be given out to users
72: # getfile(filename) : returns the contents of filename, or a -1 if it can't
73: # be found, replicates and subscribes to the file
74: # filelocation(dir,file) : returns a fairly clean absolute reference to file
75: # from the directory dir
76: # hreflocation(dir,file) : same as filelocation, but for hrefs
77: # log(domain,user,home,msg) : write to permanent log for user
78: # usection(domain,user,courseid) : output of section name/number or '' for
79: # "not in course" and '-1' for "no section"
80: # userenvironment(domain,user,what) : puts out any environment parameter
81: # for a user
82: # idput(domain,hash) : writes IDs for users from hash (name=>id,name=>id)
83: # idget(domain,array): returns hash with usernames (id=>name,id=>name) for
84: # an array of IDs
85: # idrget(domain,array): returns hash with IDs for usernames (name=>id,...) for
86: # an array of names
87: # metadata(file,entry): returns the metadata entry for a file. entry='keys'
88: # returns a comma separated list of keys
89: #
90: # 6/1/99,6/2,6/10,6/11,6/12,6/14,6/26,6/28,6/29,6/30,
91: # 7/1,7/2,7/9,7/10,7/12,7/14,7/15,7/19,
92: # 11/8,11/16,11/18,11/22,11/23,12/22,
93: # 01/06,01/13,02/24,02/28,02/29,
94: # 03/01,03/02,03/06,03/07,03/13,
95: # 04/05,05/29,05/31,06/01,
96: # 06/05,06/26 Gerd Kortemeyer
97: # 06/26 Ben Tyszka
98: # 06/30,07/15,07/17,07/18,07/20,07/21,07/22,07/25 Gerd Kortemeyer
99: # 08/14 Ben Tyszka
100: # 08/22,08/28,08/31,09/01,09/02,09/04,09/05,09/25,09/28,09/30 Gerd Kortemeyer
101: # 10/04 Gerd Kortemeyer
102: # 10/04 Guy Albertelli
103: # 10/06,10/09,10/10,10/11,10/14,10/20,10/23,10/25,10/26,10/27,10/28,10/29,
104: # 10/30,10/31,
105: # 11/2,11/14,11/15,11/16,11/20,11/21,11/22,11/25,11/27,
106: # 12/02,12/12,12/13,12/14,12/28,12/29 Gerd Kortemeyer
107: # 05/01/01 Guy Albertelli
108: # 05/01,06/01,09/01 Gerd Kortemeyer
109: # 09/01 Guy Albertelli
110: # 09/01,10/01,11/01 Gerd Kortemeyer
111: # 02/27/01 Scott Harrison
112: # 3/2 Gerd Kortemeyer
113: # 3/15,3/19 Scott Harrison
114: # 3/19,3/20 Gerd Kortemeyer
115: # 3/22,3/27,4/2,4/16,4/17 Scott Harrison
116: # 5/26,5/28 Gerd Kortemeyer
117: # 5/30 H. K. Ng
118: # 6/1 Gerd Kortemeyer
119: #
120:
121: package Apache::lonnet;
122:
123: use strict;
124: use Apache::File;
125: use LWP::UserAgent();
126: use HTTP::Headers;
127: use vars
128: qw(%perlvar %hostname %homecache %spareid %hostdom %libserv %pr %prp %fe %fd $readit %metacache);
129: use IO::Socket;
130: use GDBM_File;
131: use Apache::Constants qw(:common :http);
132: use HTML::TokeParser;
133: use Fcntl qw(:flock);
134:
135: # --------------------------------------------------------------------- Logging
136:
137: sub logthis {
138: my $message=shift;
139: my $execdir=$perlvar{'lonDaemons'};
140: my $now=time;
141: my $local=localtime($now);
142: my $fh=Apache::File->new(">>$execdir/logs/lonnet.log");
143: print $fh "$local ($$): $message\n";
144: return 1;
145: }
146:
147: sub logperm {
148: my $message=shift;
149: my $execdir=$perlvar{'lonDaemons'};
150: my $now=time;
151: my $local=localtime($now);
152: my $fh=Apache::File->new(">>$execdir/logs/lonnet.perm.log");
153: print $fh "$now:$message:$local\n";
154: return 1;
155: }
156:
157: # -------------------------------------------------- Non-critical communication
158: sub subreply {
159: my ($cmd,$server)=@_;
160: my $peerfile="$perlvar{'lonSockDir'}/$server";
161: my $client=IO::Socket::UNIX->new(Peer =>"$peerfile",
162: Type => SOCK_STREAM,
163: Timeout => 10)
164: or return "con_lost";
165: print $client "$cmd\n";
166: my $answer=<$client>;
167: if (!$answer) { $answer="con_lost"; }
168: chomp($answer);
169: return $answer;
170: }
171:
172: sub reply {
173: my ($cmd,$server)=@_;
174: my $answer=subreply($cmd,$server);
175: if ($answer eq 'con_lost') { $answer=subreply($cmd,$server); }
176: if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
177: &logthis("<font color=blue>WARNING:".
178: " $cmd to $server returned $answer</font>");
179: }
180: return $answer;
181: }
182:
183: # ----------------------------------------------------------- Send USR1 to lonc
184:
185: sub reconlonc {
186: my $peerfile=shift;
187: &logthis("Trying to reconnect for $peerfile");
188: my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
189: if (my $fh=Apache::File->new("$loncfile")) {
190: my $loncpid=<$fh>;
191: chomp($loncpid);
192: if (kill 0 => $loncpid) {
193: &logthis("lonc at pid $loncpid responding, sending USR1");
194: kill USR1 => $loncpid;
195: sleep 1;
196: if (-e "$peerfile") { return; }
197: &logthis("$peerfile still not there, give it another try");
198: sleep 5;
199: if (-e "$peerfile") { return; }
200: &logthis(
201: "<font color=blue>WARNING: $peerfile still not there, giving up</font>");
202: } else {
203: &logthis(
204: "<font color=blue>WARNING:".
205: " lonc at pid $loncpid not responding, giving up</font>");
206: }
207: } else {
208: &logthis('<font color=blue>WARNING: lonc not running, giving up</font>');
209: }
210: }
211:
212: # ------------------------------------------------------ Critical communication
213:
214: sub critical {
215: my ($cmd,$server)=@_;
216: unless ($hostname{$server}) {
217: &logthis("<font color=blue>WARNING:".
218: " Critical message to unknown server ($server)</font>");
219: return 'no_such_host';
220: }
221: my $answer=reply($cmd,$server);
222: if ($answer eq 'con_lost') {
223: my $pingreply=reply('ping',$server);
224: &reconlonc("$perlvar{'lonSockDir'}/$server");
225: my $pongreply=reply('pong',$server);
226: &logthis("Ping/Pong for $server: $pingreply/$pongreply");
227: $answer=reply($cmd,$server);
228: if ($answer eq 'con_lost') {
229: my $now=time;
230: my $middlename=$cmd;
231: $middlename=substr($middlename,0,16);
232: $middlename=~s/\W//g;
233: my $dfilename=
234: "$perlvar{'lonSockDir'}/delayed/$now.$middlename.$server";
235: {
236: my $dfh;
237: if ($dfh=Apache::File->new(">$dfilename")) {
238: print $dfh "$cmd\n";
239: }
240: }
241: sleep 2;
242: my $wcmd='';
243: {
244: my $dfh;
245: if ($dfh=Apache::File->new("$dfilename")) {
246: $wcmd=<$dfh>;
247: }
248: }
249: chomp($wcmd);
250: if ($wcmd eq $cmd) {
251: &logthis("<font color=blue>WARNING: ".
252: "Connection buffer $dfilename: $cmd</font>");
253: &logperm("D:$server:$cmd");
254: return 'con_delayed';
255: } else {
256: &logthis("<font color=red>CRITICAL:"
257: ." Critical connection failed: $server $cmd</font>");
258: &logperm("F:$server:$cmd");
259: return 'con_failed';
260: }
261: }
262: }
263: return $answer;
264: }
265:
266: # ---------------------------------------------------------- Append Environment
267:
268: sub appenv {
269: my %newenv=@_;
270: map {
271: if (($newenv{$_}=~/^user\.role/) || ($newenv{$_}=~/^user\.priv/)) {
272: &logthis("<font color=blue>WARNING: ".
273: "Attempt to modify environment ".$_." to ".$newenv{$_});
274: delete($newenv{$_});
275: } else {
276: $ENV{$_}=$newenv{$_};
277: }
278: } keys %newenv;
279:
280: my $lockfh;
281: unless ($lockfh=Apache::File->new("$ENV{'user.environment'}")) {
282: return 'error: '.$!;
283: }
284: unless (flock($lockfh,LOCK_EX)) {
285: &logthis("<font color=blue>WARNING: ".
286: 'Could not obtain exclusive lock in appenv: '.$!);
287: $lockfh->close();
288: return 'error: '.$!;
289: }
290:
291: my @oldenv;
292: {
293: my $fh;
294: unless ($fh=Apache::File->new("$ENV{'user.environment'}")) {
295: return 'error: '.$!;
296: }
297: @oldenv=<$fh>;
298: $fh->close();
299: }
300: for (my $i=0; $i<=$#oldenv; $i++) {
301: chomp($oldenv[$i]);
302: if ($oldenv[$i] ne '') {
303: my ($name,$value)=split(/=/,$oldenv[$i]);
304: unless (defined($newenv{$name})) {
305: $newenv{$name}=$value;
306: }
307: }
308: }
309: {
310: my $fh;
311: unless ($fh=Apache::File->new(">$ENV{'user.environment'}")) {
312: return 'error';
313: }
314: my $newname;
315: foreach $newname (keys %newenv) {
316: print $fh "$newname=$newenv{$newname}\n";
317: }
318: $fh->close();
319: }
320:
321: $lockfh->close();
322: return 'ok';
323: }
324: # ----------------------------------------------------- Delete from Environment
325:
326: sub delenv {
327: my $delthis=shift;
328: my %newenv=();
329: if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) {
330: &logthis("<font color=blue>WARNING: ".
331: "Attempt to delete from environment ".$delthis);
332: return 'error';
333: }
334: my @oldenv;
335: {
336: my $fh;
337: unless ($fh=Apache::File->new("$ENV{'user.environment'}")) {
338: return 'error';
339: }
340: unless (flock($fh,LOCK_SH)) {
341: &logthis("<font color=blue>WARNING: ".
342: 'Could not obtain shared lock in delenv: '.$!);
343: $fh->close();
344: return 'error: '.$!;
345: }
346: @oldenv=<$fh>;
347: $fh->close();
348: }
349: {
350: my $fh;
351: unless ($fh=Apache::File->new(">$ENV{'user.environment'}")) {
352: return 'error';
353: }
354: unless (flock($fh,LOCK_EX)) {
355: &logthis("<font color=blue>WARNING: ".
356: 'Could not obtain exclusive lock in delenv: '.$!);
357: $fh->close();
358: return 'error: '.$!;
359: }
360: map {
361: unless ($_=~/^$delthis/) { print $fh $_; }
362: } @oldenv;
363: $fh->close();
364: }
365: return 'ok';
366: }
367:
368: # ------------------------------ Find server with least workload from spare.tab
369:
370: sub spareserver {
371: my $tryserver;
372: my $spareserver='';
373: my $lowestserver=100;
374: foreach $tryserver (keys %spareid) {
375: my $answer=reply('load',$tryserver);
376: if (($answer =~ /\d/) && ($answer<$lowestserver)) {
377: $spareserver="http://$hostname{$tryserver}";
378: $lowestserver=$answer;
379: }
380: }
381: return $spareserver;
382: }
383:
384: # --------- Try to authenticate user from domain's lib servers (first this one)
385:
386: sub authenticate {
387: my ($uname,$upass,$udom)=@_;
388: $upass=escape($upass);
389: if (($perlvar{'lonRole'} eq 'library') &&
390: ($udom eq $perlvar{'lonDefDomain'})) {
391: my $answer=reply("encrypt:auth:$udom:$uname:$upass",$perlvar{'lonHostID'});
392: if ($answer =~ /authorized/) {
393: if ($answer eq 'authorized') {
394: &logthis("User $uname at $udom authorized by local server");
395: return $perlvar{'lonHostID'};
396: }
397: if ($answer eq 'non_authorized') {
398: &logthis("User $uname at $udom rejected by local server");
399: return 'no_host';
400: }
401: }
402: }
403:
404: my $tryserver;
405: foreach $tryserver (keys %libserv) {
406: if ($hostdom{$tryserver} eq $udom) {
407: my $answer=reply("encrypt:auth:$udom:$uname:$upass",$tryserver);
408: if ($answer =~ /authorized/) {
409: if ($answer eq 'authorized') {
410: &logthis("User $uname at $udom authorized by $tryserver");
411: return $tryserver;
412: }
413: if ($answer eq 'non_authorized') {
414: &logthis("User $uname at $udom rejected by $tryserver");
415: return 'no_host';
416: }
417: }
418: }
419: }
420: &logthis("User $uname at $udom could not be authenticated");
421: return 'no_host';
422: }
423:
424: # ---------------------- Find the homebase for a user from domain's lib servers
425:
426: sub homeserver {
427: my ($uname,$udom)=@_;
428:
429: my $index="$uname:$udom";
430: if ($homecache{$index}) { return "$homecache{$index}"; }
431:
432: my $tryserver;
433: foreach $tryserver (keys %libserv) {
434: if ($hostdom{$tryserver} eq $udom) {
435: my $answer=reply("home:$udom:$uname",$tryserver);
436: if ($answer eq 'found') {
437: $homecache{$index}=$tryserver;
438: return $tryserver;
439: }
440: }
441: }
442: return 'no_host';
443: }
444:
445: # ------------------------------------- Find the usernames behind a list of IDs
446:
447: sub idget {
448: my ($udom,@ids)=@_;
449: my %returnhash=();
450:
451: my $tryserver;
452: foreach $tryserver (keys %libserv) {
453: if ($hostdom{$tryserver} eq $udom) {
454: my $idlist=join('&',@ids);
455: $idlist=~tr/A-Z/a-z/;
456: my $reply=&reply("idget:$udom:".$idlist,$tryserver);
457: my @answer=();
458: if (($reply ne 'con_lost') && ($reply!~/^error\:/)) {
459: @answer=split(/\&/,$reply);
460: } ;
461: my $i;
462: for ($i=0;$i<=$#ids;$i++) {
463: if ($answer[$i]) {
464: $returnhash{$ids[$i]}=$answer[$i];
465: }
466: }
467: }
468: }
469: return %returnhash;
470: }
471:
472: # ------------------------------------- Find the IDs behind a list of usernames
473:
474: sub idrget {
475: my ($udom,@unames)=@_;
476: my %returnhash=();
477: map {
478: $returnhash{$_}=(&userenvironment($udom,$_,'id'))[1];
479: } @unames;
480: return %returnhash;
481: }
482:
483: # ------------------------------- Store away a list of names and associated IDs
484:
485: sub idput {
486: my ($udom,%ids)=@_;
487: my %servers=();
488: map {
489: my $uhom=&homeserver($_,$udom);
490: if ($uhom ne 'no_host') {
491: my $id=&escape($ids{$_});
492: $id=~tr/A-Z/a-z/;
493: my $unam=&escape($_);
494: if ($servers{$uhom}) {
495: $servers{$uhom}.='&'.$id.'='.$unam;
496: } else {
497: $servers{$uhom}=$id.'='.$unam;
498: }
499: &critical('put:'.$udom.':'.$unam.':environment:id='.$id,$uhom);
500: }
501: } keys %ids;
502: map {
503: &critical('idput:'.$udom.':'.$servers{$_},$_);
504: } keys %servers;
505: }
506:
507: # ------------------------------------- Find the section of student in a course
508:
509: sub usection {
510: my ($udom,$unam,$courseid)=@_;
511: $courseid=~s/\_/\//g;
512: $courseid=~s/^(\w)/\/$1/;
513: map {
514: my ($key,$value)=split(/\=/,$_);
515: $key=&unescape($key);
516: if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) {
517: my $section=$1;
518: if ($key eq $courseid.'_st') { $section=''; }
519: my ($dummy,$end,$start)=split(/\_/,&unescape($value));
520: my $now=time;
521: my $notactive=0;
522: if ($start) {
523: if ($now<$start) { $notactive=1; }
524: }
525: if ($end) {
526: if ($now>$end) { $notactive=1; }
527: }
528: unless ($notactive) { return $section; }
529: }
530: } split(/\&/,&reply('dump:'.$udom.':'.$unam.':roles',
531: &homeserver($unam,$udom)));
532: return '-1';
533: }
534:
535: # ------------------------------------- Read an entry from a user's environment
536:
537: sub userenvironment {
538: my ($udom,$unam,@what)=@_;
539: my %returnhash=();
540: my @answer=split(/\&/,
541: &reply('get:'.$udom.':'.$unam.':environment:'.join('&',@what),
542: &homeserver($unam,$udom)));
543: my $i;
544: for ($i=0;$i<=$#what;$i++) {
545: $returnhash{$what[$i]}=&unescape($answer[$i]);
546: }
547: return %returnhash;
548: }
549:
550: # ----------------------------- Subscribe to a resource, return URL if possible
551:
552: sub subscribe {
553: my $fname=shift;
554: my $author=$fname;
555: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
556: my ($udom,$uname)=split(/\//,$author);
557: my $home=homeserver($uname,$udom);
558: if (($home eq 'no_host') || ($home eq $perlvar{'lonHostID'})) {
559: return 'not_found';
560: }
561: my $answer=reply("sub:$fname",$home);
562: if (($answer eq 'con_lost') || ($answer eq 'rejected')) {
563: $answer.=' by '.$home;
564: }
565: return $answer;
566: }
567:
568: # -------------------------------------------------------------- Replicate file
569:
570: sub repcopy {
571: my $filename=shift;
572: $filename=~s/\/+/\//g;
573: my $transname="$filename.in.transfer";
574: if ((-e $filename) || (-e $transname)) { return OK; }
575: my $remoteurl=subscribe($filename);
576: if ($remoteurl =~ /^con_lost by/) {
577: &logthis("Subscribe returned $remoteurl: $filename");
578: return HTTP_SERVICE_UNAVAILABLE;
579: } elsif ($remoteurl eq 'not_found') {
580: &logthis("Subscribe returned not_found: $filename");
581: return HTTP_NOT_FOUND;
582: } elsif ($remoteurl =~ /^rejected by/) {
583: &logthis("Subscribe returned $remoteurl: $filename");
584: return FORBIDDEN;
585: } elsif ($remoteurl eq 'directory') {
586: return OK;
587: } else {
588: my @parts=split(/\//,$filename);
589: my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
590: if ($path ne "$perlvar{'lonDocRoot'}/res") {
591: &logthis("Malconfiguration for replication: $filename");
592: return HTTP_BAD_REQUEST;
593: }
594: my $count;
595: for ($count=5;$count<$#parts;$count++) {
596: $path.="/$parts[$count]";
597: if ((-e $path)!=1) {
598: mkdir($path,0777);
599: }
600: }
601: my $ua=new LWP::UserAgent;
602: my $request=new HTTP::Request('GET',"$remoteurl");
603: my $response=$ua->request($request,$transname);
604: if ($response->is_error()) {
605: unlink($transname);
606: my $message=$response->status_line;
607: &logthis("<font color=blue>WARNING:"
608: ." LWP get: $message: $filename</font>");
609: return HTTP_SERVICE_UNAVAILABLE;
610: } else {
611: if ($remoteurl!~/\.meta$/) {
612: my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');
613: my $mresponse=$ua->request($mrequest,$filename.'.meta');
614: if ($mresponse->is_error()) {
615: unlink($filename.'.meta');
616: &logthis(
617: "<font color=yellow>INFO: No metadata: $filename</font>");
618: }
619: }
620: rename($transname,$filename);
621: return OK;
622: }
623: }
624: }
625:
626: # --------------------------------------------------------- Server Side Include
627:
628: sub ssi {
629:
630: my ($fn,%form)=@_;
631:
632: my $ua=new LWP::UserAgent;
633:
634: my $request;
635:
636: if (%form) {
637: $request=new HTTP::Request('POST',"http://".$ENV{'HTTP_HOST'}.$fn);
638: $request->content(join '&', map { "$_=$form{$_}" } keys %form);
639: } else {
640: $request=new HTTP::Request('GET',"http://".$ENV{'HTTP_HOST'}.$fn);
641: }
642:
643: $request->header(Cookie => $ENV{'HTTP_COOKIE'});
644: my $response=$ua->request($request);
645:
646: return $response->content;
647: }
648:
649: # ------------------------------------------------------------------------- Log
650:
651: sub log {
652: my ($dom,$nam,$hom,$what)=@_;
653: return critical("log:$dom:$nam:$what",$hom);
654: }
655:
656: # --------------------------------------------- Set Expire Date for Spreadsheet
657:
658: sub expirespread {
659: my ($uname,$udom,$stype,$usymb)=@_;
660: my $cid=$ENV{'request.course.id'};
661: if ($cid) {
662: my $now=time;
663: my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
664: return &reply('put:'.$ENV{'course.'.$cid.'.domain'}.':'.
665: $ENV{'course.'.$cid.'.num'}.
666: ':nohist_expirationdates:'.
667: &escape($key).'='.$now,
668: $ENV{'course.'.$cid.'.home'})
669: }
670: return 'ok';
671: }
672:
673: # ----------------------------------------------------- Devalidate Spreadsheets
674:
675: sub devalidate {
676: my $symb=shift;
677: my $cid=$ENV{'request.course.id'};
678: if ($cid) {
679: my $key=$ENV{'user.name'}.':'.$ENV{'user.domain'}.':';
680: my $status=
681: &reply('del:'.$ENV{'course.'.$cid.'.domain'}.':'.
682: $ENV{'course.'.$cid.'.num'}.
683: ':nohist_calculatedsheets:'.
684: &escape($key.'studentcalc:'),
685: $ENV{'course.'.$cid.'.home'})
686: .' '.
687: &reply('del:'.$ENV{'user.domain'}.':'.
688: $ENV{'user.name'}.
689: ':nohist_calculatedsheets_'.$cid.':'.
690: &escape($key.'assesscalc:'.$symb),
691: $ENV{'user.home'});
692: unless ($status eq 'ok ok') {
693: &logthis('Could not devalidate spreadsheet '.
694: $ENV{'user.name'}.' at '.$ENV{'user.domain'}.' for '.
695: $symb.': '.$status);
696: }
697: }
698: }
699:
700: # ----------------------------------------------------------------------- Store
701:
702: sub store {
703: my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
704: my $home='';
705:
706: if ($stuname) {
707: $home=&homeserver($stuname,$domain);
708: }
709:
710: if (!$symb) { unless ($symb=&symbread()) { return ''; } }
711:
712: &devalidate($symb);
713:
714: $symb=escape($symb);
715: if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { return ''; } }
716: if (!$domain) { $domain=$ENV{'user.domain'}; }
717: if (!$stuname) { $stuname=$ENV{'user.name'}; }
718: if (!$home) { $home=$ENV{'user.home'}; }
719: my $namevalue='';
720: map {
721: $namevalue.=escape($_).'='.escape($$storehash{$_}).'&';
722: } keys %$storehash;
723: $namevalue=~s/\&$//;
724: return reply("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
725: }
726:
727: # -------------------------------------------------------------- Critical Store
728:
729: sub cstore {
730: my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
731: my $home='';
732:
733: if ($stuname) {
734: $home=&homeserver($stuname,$domain);
735: }
736:
737: if (!$symb) { unless ($symb=&symbread()) { return ''; } }
738:
739: &devalidate($symb);
740:
741: $symb=escape($symb);
742: if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { return ''; } }
743: if (!$domain) { $domain=$ENV{'user.domain'}; }
744: if (!$stuname) { $stuname=$ENV{'user.name'}; }
745: if (!$home) { $home=$ENV{'user.home'}; }
746:
747: my $namevalue='';
748: map {
749: $namevalue.=escape($_).'='.escape($$storehash{$_}).'&';
750: } keys %$storehash;
751: $namevalue=~s/\&$//;
752: return critical("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
753: }
754:
755: # --------------------------------------------------------------------- Restore
756:
757: sub restore {
758: my ($symb,$namespace,$domain,$stuname) = @_;
759: my $home='';
760:
761: if ($stuname) {
762: $home=&homeserver($stuname,$domain);
763: }
764:
765: if (!$symb) {
766: unless ($symb=escape(&symbread())) { return ''; }
767: } else {
768: $symb=&escape($symb);
769: }
770: if (!$namespace) { unless ($namespace=$ENV{'request.course.id'}) { return ''; } }
771: if (!$domain) { $domain=$ENV{'user.domain'}; }
772: if (!$stuname) { $stuname=$ENV{'user.name'}; }
773: if (!$home) { $home=$ENV{'user.home'}; }
774: my $answer=&reply("restore:$domain:$stuname:$namespace:$symb","$home");
775:
776: my %returnhash=();
777: map {
778: my ($name,$value)=split(/\=/,$_);
779: $returnhash{&unescape($name)}=&unescape($value);
780: } split(/\&/,$answer);
781: my $version;
782: for ($version=1;$version<=$returnhash{'version'};$version++) {
783: map {
784: $returnhash{$_}=$returnhash{$version.':'.$_};
785: } split(/\:/,$returnhash{$version.':keys'});
786: }
787: return %returnhash;
788: }
789:
790: # ---------------------------------------------------------- Course Description
791:
792: sub coursedescription {
793: my $courseid=shift;
794: $courseid=~s/^\///;
795: $courseid=~s/\_/\//g;
796: my ($cdomain,$cnum)=split(/\//,$courseid);
797: my $chome=&homeserver($cnum,$cdomain);
798: if ($chome ne 'no_host') {
799: my %returnhash=&dump('environment',$cdomain,$cnum);
800: if (!exists($returnhash{'con_lost'})) {
801: my $normalid=$cdomain.'_'.$cnum;
802: my %envhash=();
803: $returnhash{'home'}= $chome;
804: $returnhash{'domain'} = $cdomain;
805: $returnhash{'num'} = $cnum;
806: while (my ($name,$value) = each %returnhash) {
807: $envhash{'course.'.$normalid.'.'.$name}=$value;
808: }
809: $returnhash{'url'}='/res/'.declutter($returnhash{'url'});
810: $returnhash{'fn'}=$perlvar{'lonDaemons'}.'/tmp/'.
811: $ENV{'user.name'}.'_'.$cdomain.'_'.$cnum;
812: $envhash{'course.'.$normalid.'.last_cache'}=time;
813: $envhash{'course.'.$normalid.'.home'}=$chome;
814: $envhash{'course.'.$normalid.'.domain'}=$cdomain;
815: $envhash{'course.'.$normalid.'.num'}=$cnum;
816: &appenv(%envhash);
817: return %returnhash;
818: }
819: }
820: return ();
821: }
822:
823: # -------------------------------------------------------- Get user privileges
824:
825: sub rolesinit {
826: my ($domain,$username,$authhost)=@_;
827: my $rolesdump=reply("dump:$domain:$username:roles",$authhost);
828: if (($rolesdump eq 'con_lost') || ($rolesdump eq '')) { return ''; }
829: my %allroles=();
830: my %thesepriv=();
831: my $now=time;
832: my $userroles="user.login.time=$now\n";
833: my $thesestr;
834:
835: if ($rolesdump ne '') {
836: map {
837: if ($_!~/^rolesdef\&/) {
838: my ($area,$role)=split(/=/,$_);
839: $area=~s/\_\w\w$//;
840: my ($trole,$tend,$tstart)=split(/_/,$role);
841: $userroles.='user.role.'.$trole.'.'.$area.'='.
842: $tstart.'.'.$tend."\n";
843: if ($tend!=0) {
844: if ($tend<$now) {
845: $trole='';
846: }
847: }
848: if ($tstart!=0) {
849: if ($tstart>$now) {
850: $trole='';
851: }
852: }
853: if (($area ne '') && ($trole ne '')) {
854: my $spec=$trole.'.'.$area;
855: my ($tdummy,$tdomain,$trest)=split(/\//,$area);
856: if ($trole =~ /^cr\//) {
857: my ($rdummy,$rdomain,$rauthor,$rrole)=split(/\//,$trole);
858: my $homsvr=homeserver($rauthor,$rdomain);
859: if ($hostname{$homsvr} ne '') {
860: my $roledef=
861: reply("get:$rdomain:$rauthor:roles:rolesdef_$rrole",
862: $homsvr);
863: if (($roledef ne 'con_lost') && ($roledef ne '')) {
864: my ($syspriv,$dompriv,$coursepriv)=
865: split(/\_/,unescape($roledef));
866: $allroles{'cm./'}.=':'.$syspriv;
867: $allroles{$spec.'./'}.=':'.$syspriv;
868: if ($tdomain ne '') {
869: $allroles{'cm./'.$tdomain.'/'}.=':'.$dompriv;
870: $allroles{$spec.'./'.$tdomain.'/'}.=':'.$dompriv;
871: if ($trest ne '') {
872: $allroles{'cm.'.$area}.=':'.$coursepriv;
873: $allroles{$spec.'.'.$area}.=':'.$coursepriv;
874: }
875: }
876: }
877: }
878: } else {
879: $allroles{'cm./'}.=':'.$pr{$trole.':s'};
880: $allroles{$spec.'./'}.=':'.$pr{$trole.':s'};
881: if ($tdomain ne '') {
882: $allroles{'cm./'.$tdomain.'/'}.=':'.$pr{$trole.':d'};
883: $allroles{$spec.'./'.$tdomain.'/'}.=':'.$pr{$trole.':d'};
884: if ($trest ne '') {
885: $allroles{'cm.'.$area}.=':'.$pr{$trole.':c'};
886: $allroles{$spec.'.'.$area}.=':'.$pr{$trole.':c'};
887: }
888: }
889: }
890: }
891: }
892: } split(/&/,$rolesdump);
893: my $adv=0;
894: my $author=0;
895: map {
896: %thesepriv=();
897: if ($_!~/^st/) { $adv=1; }
898: if (($_=~/^au/) || ($_=~/^ca/)) { $author=1; }
899: map {
900: if ($_ ne '') {
901: my ($privilege,$restrictions)=split(/&/,$_);
902: if ($restrictions eq '') {
903: $thesepriv{$privilege}='F';
904: } else {
905: if ($thesepriv{$privilege} ne 'F') {
906: $thesepriv{$privilege}.=$restrictions;
907: }
908: }
909: }
910: } split(/:/,$allroles{$_});
911: $thesestr='';
912: map { $thesestr.=':'.$_.'&'.$thesepriv{$_}; } keys %thesepriv;
913: $userroles.='user.priv.'.$_.'='.$thesestr."\n";
914: } keys %allroles;
915: $userroles.='user.adv='.$adv."\n".
916: 'user.author='.$author."\n";
917: $ENV{'user.adv'}=$adv;
918: }
919: return $userroles;
920: }
921:
922: # --------------------------------------------------------------- get interface
923:
924: sub get {
925: my ($namespace,$storearr,$udomain,$uname)=@_;
926: my $items='';
927: map {
928: $items.=escape($_).'&';
929: } @$storearr;
930: $items=~s/\&$//;
931: if (!$udomain) { $udomain=$ENV{'user.domain'}; }
932: if (!$uname) { $uname=$ENV{'user.name'}; }
933: my $uhome=&homeserver($uname,$udomain);
934:
935: my $rep=reply("get:$udomain:$uname:$namespace:$items",$uhome);
936: my @pairs=split(/\&/,$rep);
937: my %returnhash=();
938: my $i=0;
939: map {
940: $returnhash{$_}=unescape($pairs[$i]);
941: $i++;
942: } @$storearr;
943: return %returnhash;
944: }
945:
946: # --------------------------------------------------------------- del interface
947:
948: sub del {
949: my ($namespace,@storearr)=@_;
950: my $items='';
951: map {
952: $items.=escape($_).'&';
953: } @storearr;
954: $items=~s/\&$//;
955: return reply("del:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
956: $ENV{'user.home'});
957: }
958:
959: # -------------------------------------------------------------- dump interface
960:
961: sub dump {
962: my ($namespace,$udomain,$uname)=@_;
963: if (!$udomain) { $udomain=$ENV{'user.domain'}; }
964: if (!$uname) { $uname=$ENV{'user.name'}; }
965: my $uhome=&homeserver($uname,$udomain);
966: my $rep=reply("dump:$udomain:$uname:$namespace",$uhome);
967: my @pairs=split(/\&/,$rep);
968: my %returnhash=();
969: map {
970: my ($key,$value)=split(/=/,$_);
971: $returnhash{unescape($key)}=unescape($value);
972: } @pairs;
973: return %returnhash;
974: }
975:
976: # --------------------------------------------------------------- put interface
977:
978: sub put {
979: my ($namespace,%storehash)=@_;
980: my $items='';
981: map {
982: $items.=escape($_).'='.escape($storehash{$_}).'&';
983: } keys %storehash;
984: $items=~s/\&$//;
985: return reply("put:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
986: $ENV{'user.home'});
987: }
988:
989: # ------------------------------------------------------ critical put interface
990:
991: sub cput {
992: my ($namespace,%storehash)=@_;
993: my $items='';
994: map {
995: $items.=escape($_).'='.escape($storehash{$_}).'&';
996: } keys %storehash;
997: $items=~s/\&$//;
998: return critical
999: ("put:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
1000: $ENV{'user.home'});
1001: }
1002:
1003: # -------------------------------------------------------------- eget interface
1004:
1005: sub eget {
1006: my ($namespace,@storearr)=@_;
1007: my $items='';
1008: map {
1009: $items.=escape($_).'&';
1010: } @storearr;
1011: $items=~s/\&$//;
1012: my $rep=reply("eget:$ENV{'user.domain'}:$ENV{'user.name'}:$namespace:$items",
1013: $ENV{'user.home'});
1014: my @pairs=split(/\&/,$rep);
1015: my %returnhash=();
1016: my $i=0;
1017: map {
1018: $returnhash{$_}=unescape($pairs[$i]);
1019: $i++;
1020: } @storearr;
1021: return %returnhash;
1022: }
1023:
1024: # ------------------------------------------------- Check for a user privilege
1025:
1026: sub allowed {
1027: my ($priv,$uri)=@_;
1028: $uri=&declutter($uri);
1029:
1030: # Free bre access to adm and meta resources
1031:
1032: if ((($uri=~/^adm\//) || ($uri=~/\.meta$/)) && ($priv eq 'bre')) {
1033: return 'F';
1034: }
1035:
1036: my $thisallowed='';
1037: my $statecond=0;
1038: my $courseprivid='';
1039:
1040: # Course
1041:
1042: if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'}=~/$priv\&([^\:]*)/) {
1043: $thisallowed.=$1;
1044: }
1045:
1046: # Domain
1047:
1048: if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.(split(/\//,$uri))[0].'/'}
1049: =~/$priv\&([^\:]*)/) {
1050: $thisallowed.=$1;
1051: }
1052:
1053: # Course: uri itself is a course
1054: my $courseuri=$uri;
1055: $courseuri=~s/\_(\d)/\/$1/;
1056: $courseuri=~s/^([^\/])/\/$1/;
1057:
1058: if ($ENV{'user.priv.'.$ENV{'request.role'}.'.'.$courseuri}
1059: =~/$priv\&([^\:]*)/) {
1060: $thisallowed.=$1;
1061: }
1062:
1063: # Full access at system, domain or course-wide level? Exit.
1064:
1065: if ($thisallowed=~/F/) {
1066: return 'F';
1067: }
1068:
1069: # If this is generating or modifying users, exit with special codes
1070:
1071: if (':csu:cdc:ccc:cin:cta:cep:ccr:cst:cad:cli:cau:cdg:'=~/\:$priv\:/) {
1072: return $thisallowed;
1073: }
1074: #
1075: # Gathered so far: system, domain and course wide privileges
1076: #
1077: # Course: See if uri or referer is an individual resource that is part of
1078: # the course
1079:
1080: if ($ENV{'request.course.id'}) {
1081: $courseprivid=$ENV{'request.course.id'};
1082: if ($ENV{'request.course.sec'}) {
1083: $courseprivid.='/'.$ENV{'request.course.sec'};
1084: }
1085: $courseprivid=~s/\_/\//;
1086: my $checkreferer=1;
1087: my @uriparts=split(/\//,$uri);
1088: my $filename=$uriparts[$#uriparts];
1089: my $pathname=$uri;
1090: $pathname=~s/\/$filename$//;
1091: if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
1092: /\&$filename\:([\d\|]+)\&/) {
1093: $statecond=$1;
1094: if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
1095: =~/$priv\&([^\:]*)/) {
1096: $thisallowed.=$1;
1097: $checkreferer=0;
1098: }
1099: }
1100:
1101: if (($ENV{'HTTP_REFERER'}) && ($checkreferer)) {
1102: my $refuri=$ENV{'HTTP_REFERER'};
1103: $refuri=~s/^http\:\/\/$ENV{'request.host'}//i;
1104: $refuri=&declutter($refuri);
1105: my @uriparts=split(/\//,$refuri);
1106: my $filename=$uriparts[$#uriparts];
1107: my $pathname=$refuri;
1108: $pathname=~s/\/$filename$//;
1109: my @filenameparts=split(/\./,$uri);
1110: if (&fileembstyle($filenameparts[$#filenameparts]) ne 'ssi') {
1111: if ($ENV{'acc.res.'.$ENV{'request.course.id'}.'.'.$pathname}=~
1112: /\&$filename\:([\d\|]+)\&/) {
1113: my $refstatecond=$1;
1114: if ($ENV{'user.priv.'.$ENV{'request.role'}.'./'.$courseprivid}
1115: =~/$priv\&([^\:]*)/) {
1116: $thisallowed.=$1;
1117: $uri=$refuri;
1118: $statecond=$refstatecond;
1119: }
1120: }
1121: }
1122: }
1123: }
1124:
1125: #
1126: # Gathered now: all privileges that could apply, and condition number
1127: #
1128: #
1129: # Full or no access?
1130: #
1131:
1132: if ($thisallowed=~/F/) {
1133: return 'F';
1134: }
1135:
1136: unless ($thisallowed) {
1137: return '';
1138: }
1139:
1140: # Restrictions exist, deal with them
1141: #
1142: # C:according to course preferences
1143: # R:according to resource settings
1144: # L:unless locked
1145: # X:according to user session state
1146: #
1147:
1148: # Possibly locked functionality, check all courses
1149: # Locks might take effect only after 10 minutes cache expiration for other
1150: # courses, and 2 minutes for current course
1151:
1152: my $envkey;
1153: if ($thisallowed=~/L/) {
1154: foreach $envkey (keys %ENV) {
1155: if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) {
1156: my $courseid=$2;
1157: my $roleid=$1.'.'.$2;
1158: $courseid=~s/^\///;
1159: my $expiretime=600;
1160: if ($ENV{'request.role'} eq $roleid) {
1161: $expiretime=120;
1162: }
1163: my ($cdom,$cnum,$csec)=split(/\//,$courseid);
1164: my $prefix='course.'.$cdom.'_'.$cnum.'.';
1165: if ((time-$ENV{$prefix.'last_cache'})>$expiretime) {
1166: &coursedescription($courseid);
1167: }
1168: if (($ENV{$prefix.'res.'.$uri.'.lock.sections'}=~/\,$csec\,/)
1169: || ($ENV{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
1170: if ($ENV{$prefix.'res.'.$uri.'.lock.expire'}>time) {
1171: &log($ENV{'user.domain'},$ENV{'user.name'},
1172: $ENV{'user.host'},
1173: 'Locked by res: '.$priv.' for '.$uri.' due to '.
1174: $cdom.'/'.$cnum.'/'.$csec.' expire '.
1175: $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
1176: return '';
1177: }
1178: }
1179: if (($ENV{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,$csec\,/)
1180: || ($ENV{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
1181: if ($ENV{'priv.'.$priv.'.lock.expire'}>time) {
1182: &log($ENV{'user.domain'},$ENV{'user.name'},
1183: $ENV{'user.host'},
1184: 'Locked by priv: '.$priv.' for '.$uri.' due to '.
1185: $cdom.'/'.$cnum.'/'.$csec.' expire '.
1186: $ENV{$prefix.'priv.'.$priv.'.lock.expire'});
1187: return '';
1188: }
1189: }
1190: }
1191: }
1192: }
1193:
1194: #
1195: # Rest of the restrictions depend on selected course
1196: #
1197:
1198: unless ($ENV{'request.course.id'}) {
1199: return '1';
1200: }
1201:
1202: #
1203: # Now user is definitely in a course
1204: #
1205:
1206:
1207: # Course preferences
1208:
1209: if ($thisallowed=~/C/) {
1210: my $rolecode=(split(/\./,$ENV{'request.role'}))[0];
1211: if ($ENV{'course.'.$ENV{'request.course.id'}.'.'.$priv.'.roles.denied'}
1212: =~/\,$rolecode\,/) {
1213: &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
1214: 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
1215: $ENV{'request.course.id'});
1216: return '';
1217: }
1218: }
1219:
1220: # Resource preferences
1221:
1222: if ($thisallowed=~/R/) {
1223: my $rolecode=(split(/\./,$ENV{'request.role'}))[0];
1224: my $filename=$perlvar{'lonDocRoot'}.'/res/'.$uri.'.meta';
1225: if (-e $filename) {
1226: my @content;
1227: {
1228: my $fh=Apache::File->new($filename);
1229: @content=<$fh>;
1230: }
1231: if (join('',@content)=~
1232: /\<roledeny[^\>]*\>[^\<]*$rolecode[^\<]*\<\/roledeny\>/) {
1233: &log($ENV{'user.domain'},$ENV{'user.name'},$ENV{'user.host'},
1234: 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode);
1235: return '';
1236:
1237: }
1238: }
1239: }
1240:
1241: # Restricted by state?
1242:
1243: if ($thisallowed=~/X/) {
1244: if (&condval($statecond)) {
1245: return '2';
1246: } else {
1247: return '';
1248: }
1249: }
1250:
1251: return 'F';
1252: }
1253:
1254: # ----------------------------------------------------------------- Define Role
1255:
1256: sub definerole {
1257: if (allowed('mcr','/')) {
1258: my ($rolename,$sysrole,$domrole,$courole)=@_;
1259: map {
1260: my ($crole,$cqual)=split(/\&/,$_);
1261: if ($pr{'cr:s'}!~/$crole/) { return "refused:s:$crole"; }
1262: if ($pr{'cr:s'}=~/$crole\&/) {
1263: if ($pr{'cr:s'}!~/$crole\&\w*$cqual/) {
1264: return "refused:s:$crole&$cqual";
1265: }
1266: }
1267: } split('/',$sysrole);
1268: map {
1269: my ($crole,$cqual)=split(/\&/,$_);
1270: if ($pr{'cr:d'}!~/$crole/) { return "refused:d:$crole"; }
1271: if ($pr{'cr:d'}=~/$crole\&/) {
1272: if ($pr{'cr:d'}!~/$crole\&\w*$cqual/) {
1273: return "refused:d:$crole&$cqual";
1274: }
1275: }
1276: } split('/',$domrole);
1277: map {
1278: my ($crole,$cqual)=split(/\&/,$_);
1279: if ($pr{'cr:c'}!~/$crole/) { return "refused:c:$crole"; }
1280: if ($pr{'cr:c'}=~/$crole\&/) {
1281: if ($pr{'cr:c'}!~/$crole\&\w*$cqual/) {
1282: return "refused:c:$crole&$cqual";
1283: }
1284: }
1285: } split('/',$courole);
1286: my $command="encrypt:rolesput:$ENV{'user.domain'}:$ENV{'user.name'}:".
1287: "$ENV{'user.domain'}:$ENV{'user.name'}:".
1288: "rolesdef_$rolename=".
1289: escape($sysrole.'_'.$domrole.'_'.$courole);
1290: return reply($command,$ENV{'user.home'});
1291: } else {
1292: return 'refused';
1293: }
1294: }
1295:
1296: # ---------------- Make a metadata query against the network of library servers
1297:
1298: sub metadata_query {
1299: my ($query,$custom,$customshow)=@_;
1300: # need to put in a library server loop here and return a hash
1301: my %rhash;
1302: for my $server (keys %libserv) {
1303: unless ($custom or $customshow) {
1304: my $reply=&reply("querysend:".&escape($query),$server);
1305: $rhash{$server}=$reply;
1306: }
1307: else {
1308: my $reply=&reply("querysend:".&escape($query).':'.
1309: &escape($custom).':'.&escape($customshow),
1310: $server);
1311: $rhash{$server}=$reply;
1312: }
1313: }
1314: return \%rhash;
1315: }
1316:
1317: # ------------------------------------------------------------------ Plain Text
1318:
1319: sub plaintext {
1320: my $short=shift;
1321: return $prp{$short};
1322: }
1323:
1324: # ------------------------------------------------------------------ Plain Text
1325:
1326: sub fileembstyle {
1327: my $ending=shift;
1328: return $fe{$ending};
1329: }
1330:
1331: # ------------------------------------------------------------ Description Text
1332:
1333: sub filedescription {
1334: my $ending=shift;
1335: return $fd{$ending};
1336: }
1337:
1338: # ----------------------------------------------------------------- Assign Role
1339:
1340: sub assignrole {
1341: my ($udom,$uname,$url,$role,$end,$start)=@_;
1342: my $mrole;
1343: if ($role =~ /^cr\//) {
1344: unless (&allowed('ccr',$url)) {
1345: &logthis('Refused custom assignrole: '.
1346: $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '.
1347: $ENV{'user.name'}.' at '.$ENV{'user.domain'});
1348: return 'refused';
1349: }
1350: $mrole='cr';
1351: } else {
1352: my $cwosec=$url;
1353: $cwosec=~s/^\/(\w+)\/(\w+)\/.*/$1\/$2/;
1354: unless (&allowed('c'.$role,$cwosec)) {
1355: &logthis('Refused assignrole: '.
1356: $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '.
1357: $ENV{'user.name'}.' at '.$ENV{'user.domain'});
1358: return 'refused';
1359: }
1360: $mrole=$role;
1361: }
1362: my $command="encrypt:rolesput:$ENV{'user.domain'}:$ENV{'user.name'}:".
1363: "$udom:$uname:$url".'_'."$mrole=$role";
1364: if ($end) { $command.='_'.$end; }
1365: if ($start) {
1366: if ($end) {
1367: $command.='_'.$start;
1368: } else {
1369: $command.='_0_'.$start;
1370: }
1371: }
1372: return &reply($command,&homeserver($uname,$udom));
1373: }
1374:
1375: # --------------------------------------------------------------- Modify a user
1376:
1377:
1378: sub modifyuser {
1379: my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene)=@_;
1380: &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.
1381: $umode.', '.$first.', '.$middle.', '.
1382: $last.', '.$gene.' by '.
1383: $ENV{'user.name'}.' at '.$ENV{'user.domain'});
1384: my $uhome=&homeserver($uname,$udom);
1385: # ----------------------------------------------------------------- Create User
1386: if (($uhome eq 'no_host') && ($umode) && ($upass)) {
1387: my $unhome='';
1388: if ($ENV{'course.'.$ENV{'request.course.id'}.'.domain'} eq $udom) {
1389: $unhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'};
1390: } else {
1391: my $tryserver;
1392: my $loadm=10000000;
1393: foreach $tryserver (keys %libserv) {
1394: if ($hostdom{$tryserver} eq $udom) {
1395: my $answer=reply('load',$tryserver);
1396: if (($answer=~/\d+/) && ($answer<$loadm)) {
1397: $loadm=$answer;
1398: $unhome=$tryserver;
1399: }
1400: }
1401: }
1402: }
1403: if (($unhome eq '') || ($unhome eq 'no_host')) {
1404: return 'error: find home';
1405: }
1406: my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':'.$umode.':'.
1407: &escape($upass),$unhome);
1408: unless ($reply eq 'ok') {
1409: return 'error: '.$reply;
1410: }
1411: $uhome=&homeserver($uname,$udom);
1412: if (($uhome eq '') || ($uhome eq 'no_host') || ($uhome ne $unhome)) {
1413: return 'error: verify home';
1414: }
1415: }
1416: # ---------------------------------------------------------------------- Add ID
1417: if ($uid) {
1418: $uid=~tr/A-Z/a-z/;
1419: my %uidhash=&idrget($udom,$uname);
1420: if (($uidhash{$uname}) && ($uidhash{$uname}!~/error\:/)) {
1421: unless ($uid eq $uidhash{$uname}) {
1422: return 'error: mismatch '.$uidhash{$uname}.' versus '.$uid;
1423: }
1424: } else {
1425: &idput($udom,($uname => $uid));
1426: }
1427: }
1428: # -------------------------------------------------------------- Add names, etc
1429: my $names=&reply('get:'.$udom.':'.$uname.
1430: ':environment:firstname&middlename&lastname&generation',
1431: $uhome);
1432: my ($efirst,$emiddle,$elast,$egene)=split(/\&/,$names);
1433: if ($first) { $efirst = &escape($first); }
1434: if ($middle) { $emiddle = &escape($middle); }
1435: if ($last) { $elast = &escape($last); }
1436: if ($gene) { $egene = &escape($gene); }
1437: my $reply=&reply('put:'.$udom.':'.$uname.
1438: ':environment:firstname='.$efirst.
1439: '&middlename='.$emiddle.
1440: '&lastname='.$elast.
1441: '&generation='.$egene,$uhome);
1442: if ($reply ne 'ok') {
1443: return 'error: '.$reply;
1444: }
1445: &logthis('Success modifying user '.$udom.', '.$uname.', '.$uid.', '.
1446: $umode.', '.$first.', '.$middle.', '.
1447: $last.', '.$gene.' by '.
1448: $ENV{'user.name'}.' at '.$ENV{'user.domain'});
1449: return 'ok';
1450: }
1451:
1452: # -------------------------------------------------------------- Modify student
1453:
1454: sub modifystudent {
1455: my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec,
1456: $end,$start)=@_;
1457: my $cid='';
1458: unless ($cid=$ENV{'request.course.id'}) {
1459: return 'not_in_class';
1460: }
1461: # --------------------------------------------------------------- Make the user
1462: my $reply=&modifyuser
1463: ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene);
1464: unless ($reply eq 'ok') { return $reply; }
1465: my $uhome=&homeserver($uname,$udom);
1466: if (($uhome eq '') || ($uhome eq 'no_host')) {
1467: return 'error: no such user';
1468: }
1469: # -------------------------------------------------- Add student to course list
1470: my $reply=critical('put:'.$ENV{'course.'.$cid.'.domain'}.':'.
1471: $ENV{'course.'.$cid.'.num'}.':classlist:'.
1472: &escape($uname.':'.$udom).'='.
1473: &escape($end.':'.$start),
1474: $ENV{'course.'.$cid.'.home'});
1475: unless (($reply eq 'ok') || ($reply eq 'delayed')) {
1476: return 'error: '.$reply;
1477: }
1478: # ---------------------------------------------------- Add student role to user
1479: my $uurl='/'.$cid;
1480: $uurl=~s/\_/\//g;
1481: if ($usec) {
1482: $uurl.='/'.$usec;
1483: }
1484: return &assignrole($udom,$uname,$uurl,'st',$end,$start);
1485: }
1486:
1487: # ------------------------------------------------- Write to course preferences
1488:
1489: sub writecoursepref {
1490: my ($courseid,%prefs)=@_;
1491: $courseid=~s/^\///;
1492: $courseid=~s/\_/\//g;
1493: my ($cdomain,$cnum)=split(/\//,$courseid);
1494: my $chome=homeserver($cnum,$cdomain);
1495: if (($chome eq '') || ($chome eq 'no_host')) {
1496: return 'error: no such course';
1497: }
1498: my $cstring='';
1499: map {
1500: $cstring.=escape($_).'='.escape($prefs{$_}).'&';
1501: } keys %prefs;
1502: $cstring=~s/\&$//;
1503: return reply('put:'.$cdomain.':'.$cnum.':environment:'.$cstring,$chome);
1504: }
1505:
1506: # ---------------------------------------------------------- Make/modify course
1507:
1508: sub createcourse {
1509: my ($udom,$description,$url)=@_;
1510: $url=&declutter($url);
1511: my $cid='';
1512: unless (&allowed('ccc',$ENV{'user.domain'})) {
1513: return 'refused';
1514: }
1515: unless ($udom eq $ENV{'user.domain'}) {
1516: return 'refused';
1517: }
1518: # ------------------------------------------------------------------- Create ID
1519: my $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).
1520: unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};
1521: # ----------------------------------------------- Make sure that does not exist
1522: my $uhome=&homeserver($uname,$udom);
1523: unless (($uhome eq '') || ($uhome eq 'no_host')) {
1524: $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)).
1525: unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'};
1526: $uhome=&homeserver($uname,$udom);
1527: unless (($uhome eq '') || ($uhome eq 'no_host')) {
1528: return 'error: unable to generate unique course-ID';
1529: }
1530: }
1531: # ------------------------------------------------------------- Make the course
1532: my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::',
1533: $ENV{'user.home'});
1534: unless ($reply eq 'ok') { return 'error: '.$reply; }
1535: my $uhome=&homeserver($uname,$udom);
1536: if (($uhome eq '') || ($uhome eq 'no_host')) {
1537: return 'error: no such course';
1538: }
1539: &writecoursepref($udom.'_'.$uname,
1540: ('description' => $description,
1541: 'url' => $url));
1542: return '/'.$udom.'/'.$uname;
1543: }
1544:
1545: # ---------------------------------------------------------- Assign Custom Role
1546:
1547: sub assigncustomrole {
1548: my ($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start)=@_;
1549: return &assignrole($udom,$uname,$url,'cr/'.$rdom.'/'.$rnam.'/'.$rolename,
1550: $end,$start);
1551: }
1552:
1553: # ----------------------------------------------------------------- Revoke Role
1554:
1555: sub revokerole {
1556: my ($udom,$uname,$url,$role)=@_;
1557: my $now=time;
1558: return &assignrole($udom,$uname,$url,$role,$now);
1559: }
1560:
1561: # ---------------------------------------------------------- Revoke Custom Role
1562:
1563: sub revokecustomrole {
1564: my ($udom,$uname,$url,$rdom,$rnam,$rolename)=@_;
1565: my $now=time;
1566: return &assigncustomrole($udom,$uname,$url,$rdom,$rnam,$rolename,$now);
1567: }
1568:
1569: # ------------------------------------------------------------ Directory lister
1570:
1571: sub dirlist {
1572: my $uri=shift;
1573: $uri=~s/^\///;
1574: $uri=~s/\/$//;
1575: my ($res,$udom,$uname,@rest)=split(/\//,$uri);
1576: if ($udom) {
1577: if ($uname) {
1578: my $listing=reply('ls:'.$perlvar{'lonDocRoot'}.'/'.$uri,
1579: homeserver($uname,$udom));
1580: return split(/:/,$listing);
1581: } else {
1582: my $tryserver;
1583: my %allusers=();
1584: foreach $tryserver (keys %libserv) {
1585: if ($hostdom{$tryserver} eq $udom) {
1586: my $listing=reply('ls:'.$perlvar{'lonDocRoot'}.'/res/'.$udom,
1587: $tryserver);
1588: if (($listing ne 'no_such_dir') && ($listing ne 'empty')
1589: && ($listing ne 'con_lost')) {
1590: map {
1591: my ($entry,@stat)=split(/&/,$_);
1592: $allusers{$entry}=1;
1593: } split(/:/,$listing);
1594: }
1595: }
1596: }
1597: my $alluserstr='';
1598: map {
1599: $alluserstr.=$_.'&user:';
1600: } sort keys %allusers;
1601: $alluserstr=~s/:$//;
1602: return split(/:/,$alluserstr);
1603: }
1604: } else {
1605: my $tryserver;
1606: my %alldom=();
1607: foreach $tryserver (keys %libserv) {
1608: $alldom{$hostdom{$tryserver}}=1;
1609: }
1610: my $alldomstr='';
1611: map {
1612: $alldomstr.=$perlvar{'lonDocRoot'}.'/res/'.$_.'&domain:';
1613: } sort keys %alldom;
1614: $alldomstr=~s/:$//;
1615: return split(/:/,$alldomstr);
1616: }
1617: }
1618:
1619: # -------------------------------------------------------- Value of a Condition
1620:
1621: sub directcondval {
1622: my $number=shift;
1623: if ($ENV{'user.state.'.$ENV{'request.course.id'}}) {
1624: return substr($ENV{'user.state.'.$ENV{'request.course.id'}},$number,1);
1625: } else {
1626: return 2;
1627: }
1628: }
1629:
1630: sub condval {
1631: my $condidx=shift;
1632: my $result=0;
1633: my $allpathcond='';
1634: map {
1635: if (defined($ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$_})) {
1636: $allpathcond.=
1637: '('.$ENV{'acc.cond.'.$ENV{'request.course.id'}.'.'.$_}.')|';
1638: }
1639: } split(/\|/,$condidx);
1640: $allpathcond=~s/\|$//;
1641: if ($ENV{'request.course.id'}) {
1642: if ($allpathcond) {
1643: my $operand='|';
1644: my @stack;
1645: map {
1646: if ($_ eq '(') {
1647: push @stack,($operand,$result)
1648: } elsif ($_ eq ')') {
1649: my $before=pop @stack;
1650: if (pop @stack eq '&') {
1651: $result=$result>$before?$before:$result;
1652: } else {
1653: $result=$result>$before?$result:$before;
1654: }
1655: } elsif (($_ eq '&') || ($_ eq '|')) {
1656: $operand=$_;
1657: } else {
1658: my $new=directcondval($_);
1659: if ($operand eq '&') {
1660: $result=$result>$new?$new:$result;
1661: } else {
1662: $result=$result>$new?$result:$new;
1663: }
1664: }
1665: } ($allpathcond=~/(\d+|\(|\)|\&|\|)/g);
1666: }
1667: }
1668: return $result;
1669: }
1670:
1671: # --------------------------------------------------------- Value of a Variable
1672:
1673: sub EXT {
1674: my $varname=shift;
1675: unless ($varname) { return ''; }
1676: my ($realm,$space,$qualifier,@therest)=split(/\./,$varname);
1677: my $rest;
1678: if ($therest[0]) {
1679: $rest=join('.',@therest);
1680: } else {
1681: $rest='';
1682: }
1683: my $qualifierrest=$qualifier;
1684: if ($rest) { $qualifierrest.='.'.$rest; }
1685: my $spacequalifierrest=$space;
1686: if ($qualifierrest) { $spacequalifierrest.='.'.$qualifierrest; }
1687: if ($realm eq 'user') {
1688: # --------------------------------------------------------------- user.resource
1689: if ($space eq 'resource') {
1690: my %restored=&restore();
1691: return $restored{$qualifierrest};
1692: # ----------------------------------------------------------------- user.access
1693: } elsif ($space eq 'access') {
1694: return &allowed($qualifier,$rest);
1695: # ------------------------------------------ user.preferences, user.environment
1696: } elsif (($space eq 'preferences') || ($space eq 'environment')) {
1697: return $ENV{join('.',('environment',$qualifierrest))};
1698: # ----------------------------------------------------------------- user.course
1699: } elsif ($space eq 'course') {
1700: return $ENV{join('.',('request.course',$qualifier))};
1701: # ------------------------------------------------------------------- user.role
1702: } elsif ($space eq 'role') {
1703: my ($role,$where)=split(/\./,$ENV{'request.role'});
1704: if ($qualifier eq 'value') {
1705: return $role;
1706: } elsif ($qualifier eq 'extent') {
1707: return $where;
1708: }
1709: # ----------------------------------------------------------------- user.domain
1710: } elsif ($space eq 'domain') {
1711: return $ENV{'user.domain'};
1712: # ------------------------------------------------------------------- user.name
1713: } elsif ($space eq 'name') {
1714: return $ENV{'user.name'};
1715: # ---------------------------------------------------- Any other user namespace
1716: } else {
1717: my $item=($rest)?$qualifier.'.'.$rest:$qualifier;
1718: my %reply=&get($space,[$item]);
1719: return $reply{$item};
1720: }
1721: } elsif ($realm eq 'request') {
1722: # ------------------------------------------------------------- request.browser
1723: if ($space eq 'browser') {
1724: return $ENV{'browser.'.$qualifier};
1725: # ------------------------------------------------------------ request.filename
1726: } else {
1727: return $ENV{'request.'.$spacequalifierrest};
1728: }
1729: } elsif ($realm eq 'course') {
1730: # ---------------------------------------------------------- course.description
1731: return $ENV{'course.'.$ENV{'request.course.id'}.'.'.
1732: $spacequalifierrest};
1733: } elsif ($realm eq 'resource') {
1734: if ($ENV{'request.course.id'}) {
1735: # ----------------------------------------------------- Cascading lookup scheme
1736: my $symbp=&symbread();
1737: my $mapp=(split(/\_\_\_/,$symbp))[0];
1738:
1739: my $symbparm=$symbp.'.'.$spacequalifierrest;
1740: my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
1741:
1742: my $seclevel=
1743: $ENV{'request.course.id'}.'.['.
1744: $ENV{'request.course.sec'}.'].'.$spacequalifierrest;
1745: my $seclevelr=
1746: $ENV{'request.course.id'}.'.['.
1747: $ENV{'request.course.sec'}.'].'.$symbparm;
1748: my $seclevelm=
1749: $ENV{'request.course.id'}.'.['.
1750: $ENV{'request.course.sec'}.'].'.$mapparm;
1751:
1752: my $courselevel=
1753: $ENV{'request.course.id'}.'.'.$spacequalifierrest;
1754: my $courselevelr=
1755: $ENV{'request.course.id'}.'.'.$symbparm;
1756: my $courselevelm=
1757: $ENV{'request.course.id'}.'.'.$mapparm;
1758:
1759: # ----------------------------------------------------------- first, check user
1760: my %resourcedata=get('resourcedata',
1761: [$courselevelr,$courselevelm,$courselevel]);
1762: if (($resourcedata{$courselevelr}!~/^error\:/) &&
1763: ($resourcedata{$courselevelr}!~/^con_lost/)) {
1764:
1765: if ($resourcedata{$courselevelr}) {
1766: return $resourcedata{$courselevelr}; }
1767: if ($resourcedata{$courselevelm}) {
1768: return $resourcedata{$courselevelm}; }
1769: if ($resourcedata{$courselevel}) { return $resourcedata{$courselevel}; }
1770:
1771: } else {
1772: if ($resourcedata{$courselevelr}!~/No such file/) {
1773: &logthis("<font color=blue>WARNING:".
1774: " Trying to get resource data for ".$ENV{'user.name'}." at "
1775: .$ENV{'user.domain'}.": ".$resourcedata{$courselevelr}.
1776: "</font>");
1777: }
1778: }
1779:
1780: # -------------------------------------------------------- second, check course
1781:
1782: my $reply=&reply('get:'.
1783: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'.
1784: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.
1785: ':resourcedata:'.
1786: &escape($seclevelr).'&'.&escape($seclevelm).'&'.&escape($seclevel).'&'.
1787: &escape($courselevelr).'&'.&escape($courselevelm).'&'.&escape($courselevel),
1788: $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
1789: if ($reply!~/^error\:/) {
1790: map {
1791: if ($_) { return &unescape($_); }
1792: } split(/\&/,$reply);
1793: }
1794: if (($reply=~/^con_lost/) || ($reply=~/^error\:/)) {
1795: &logthis("<font color=blue>WARNING:".
1796: " Getting ".$reply." asking for ".$varname." for ".
1797: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.
1798: ' at '.
1799: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.
1800: ' from '.
1801: $ENV{'course.'.$ENV{'request.course.id'}.'.home'}.
1802: "</font>");
1803: }
1804: # ------------------------------------------------------ third, check map parms
1805: my %parmhash=();
1806: my $thisparm='';
1807: if (tie(%parmhash,'GDBM_File',
1808: $ENV{'request.course.fn'}.'_parms.db',&GDBM_READER,0640)) {
1809: $thisparm=$parmhash{$symbparm};
1810: untie(%parmhash);
1811: }
1812: if ($thisparm) { return $thisparm; }
1813: }
1814:
1815: # --------------------------------------------- last, look in resource metadata
1816:
1817: $spacequalifierrest=~s/\./\_/;
1818: my $metadata=&metadata($ENV{'request.filename'},$spacequalifierrest);
1819: if ($metadata) { return $metadata; }
1820: $metadata=&metadata($ENV{'request.filename'},
1821: 'parameter_'.$spacequalifierrest);
1822: if ($metadata) { return $metadata; }
1823:
1824: # ---------------------------------------------------- Any other user namespace
1825: } elsif ($realm eq 'environment') {
1826: # ----------------------------------------------------------------- environment
1827: return $ENV{'environment.'.$spacequalifierrest};
1828: } elsif ($realm eq 'system') {
1829: # ----------------------------------------------------------------- system.time
1830: if ($space eq 'time') {
1831: return time;
1832: }
1833: }
1834: return '';
1835: }
1836:
1837: # ---------------------------------------------------------------- Get metadata
1838:
1839: sub metadata {
1840: my ($uri,$what)=@_;
1841:
1842: $uri=&declutter($uri);
1843: my $filename=$uri;
1844: $uri=~s/\.meta$//;
1845: unless ($metacache{$uri.':keys'}) {
1846: unless ($filename=~/\.meta$/) { $filename.='.meta'; }
1847: my $metastring=&getfile($perlvar{'lonDocRoot'}.'/res/'.$filename);
1848: my $parser=HTML::TokeParser->new(\$metastring);
1849: my $token;
1850: while ($token=$parser->get_token) {
1851: if ($token->[0] eq 'S') {
1852: my $entry=$token->[1];
1853: my $unikey=$entry;
1854: if (defined($token->[2]->{'part'})) {
1855: $unikey.='_'.$token->[2]->{'part'};
1856: }
1857: if (defined($token->[2]->{'name'})) {
1858: $unikey.='_'.$token->[2]->{'name'};
1859: }
1860: if ($metacache{$uri.':keys'}) {
1861: $metacache{$uri.':keys'}.=','.$unikey;
1862: } else {
1863: $metacache{$uri.':keys'}=$unikey;
1864: }
1865: map {
1866: $metacache{$uri.':'.$unikey.'.'.$_}=$token->[2]->{$_};
1867: } @{$token->[3]};
1868: unless (
1869: $metacache{$uri.':'.$unikey}=$parser->get_text('/'.$entry)
1870: ) { $metacache{$uri.':'.$unikey}=
1871: $metacache{$uri.':'.$unikey.'.default'};
1872: }
1873: }
1874: }
1875: }
1876: return $metacache{$uri.':'.$what};
1877: }
1878:
1879: # ------------------------------------------------- Update symbolic store links
1880:
1881: sub symblist {
1882: my ($mapname,%newhash)=@_;
1883: $mapname=declutter($mapname);
1884: my %hash;
1885: if (($ENV{'request.course.fn'}) && (%newhash)) {
1886: if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
1887: &GDBM_WRCREAT,0640)) {
1888: map {
1889: $hash{declutter($_)}=$mapname.'___'.$newhash{$_};
1890: } keys %newhash;
1891: if (untie(%hash)) {
1892: return 'ok';
1893: }
1894: }
1895: }
1896: return 'error';
1897: }
1898:
1899: # ------------------------------------------------------ Return symb list entry
1900:
1901: sub symbread {
1902: my $thisfn=shift;
1903: unless ($thisfn) {
1904: $thisfn=$ENV{'request.filename'};
1905: }
1906: $thisfn=declutter($thisfn);
1907: my %hash;
1908: my %bighash;
1909: my $syval='';
1910: if (($ENV{'request.course.fn'}) && ($thisfn)) {
1911: if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db',
1912: &GDBM_READER,0640)) {
1913: $syval=$hash{$thisfn};
1914: untie(%hash);
1915: }
1916: # ---------------------------------------------------------- There was an entry
1917: if ($syval) {
1918: unless ($syval=~/\_\d+$/) {
1919: unless ($ENV{'form.request.prefix'}=~/\.(\d+)\_$/) {
1920: &appenv('request.ambiguous' => $thisfn);
1921: return '';
1922: }
1923: $syval.=$1;
1924: }
1925: } else {
1926: # ------------------------------------------------------- Was not in symb table
1927: if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
1928: &GDBM_READER,0640)) {
1929: # ---------------------------------------------- Get ID(s) for current resource
1930: my $ids=$bighash{'ids_/res/'.$thisfn};
1931: unless ($ids) {
1932: $ids=$bighash{'ids_/'.$thisfn};
1933: }
1934: if ($ids) {
1935: # ------------------------------------------------------------------- Has ID(s)
1936: my @possibilities=split(/\,/,$ids);
1937: if ($#possibilities==0) {
1938: # ----------------------------------------------- There is only one possibility
1939: my ($mapid,$resid)=split(/\./,$ids);
1940: $syval=declutter($bighash{'map_id_'.$mapid}).'___'.$resid;
1941: } else {
1942: # ------------------------------------------ There is more than one possibility
1943: my $realpossible=0;
1944: map {
1945: my $file=$bighash{'src_'.$_};
1946: if (&allowed('bre',$file)) {
1947: my ($mapid,$resid)=split(/\./,$_);
1948: if ($bighash{'map_type_'.$mapid} ne 'page') {
1949: $realpossible++;
1950: $syval=declutter($bighash{'map_id_'.$mapid}).
1951: '___'.$resid;
1952: }
1953: }
1954: } @possibilities;
1955: if ($realpossible!=1) { $syval=''; }
1956: }
1957: }
1958: untie(%bighash)
1959: }
1960: }
1961: if ($syval) {
1962: return $syval.'___'.$thisfn;
1963: }
1964: }
1965: &appenv('request.ambiguous' => $thisfn);
1966: return '';
1967: }
1968:
1969: # ---------------------------------------------------------- Return random seed
1970:
1971: sub numval {
1972: my $txt=shift;
1973: $txt=~tr/A-J/0-9/;
1974: $txt=~tr/a-j/0-9/;
1975: $txt=~tr/K-T/0-9/;
1976: $txt=~tr/k-t/0-9/;
1977: $txt=~tr/U-Z/0-5/;
1978: $txt=~tr/u-z/0-5/;
1979: $txt=~s/\D//g;
1980: return int($txt);
1981: }
1982:
1983: sub rndseed {
1984: my $symb;
1985: unless ($symb=&symbread()) { return time; }
1986: {
1987: use integer;
1988: my $symbchck=unpack("%32C*",$symb) << 27;
1989: my $symbseed=numval($symb) << 22;
1990: my $namechck=unpack("%32C*",$ENV{'user.name'}) << 17;
1991: my $nameseed=numval($ENV{'user.name'}) << 12;
1992: my $domainseed=unpack("%32C*",$ENV{'user.domain'}) << 7;
1993: my $courseseed=unpack("%32C*",$ENV{'request.course.id'});
1994: my $num=$symbseed+$nameseed+$domainseed+$courseseed+$namechck+$symbchck;
1995: #uncommenting these lines can break things!
1996: #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
1997: #&Apache::lonxml::debug("rndseed :$num:$symb");
1998: return $num;
1999: }
2000: }
2001:
2002: sub ireceipt {
2003: my ($funame,$fudom,$fucourseid,$fusymb)=@_;
2004: my $cuname=unpack("%32C*",$funame);
2005: my $cudom=unpack("%32C*",$fudom);
2006: my $cucourseid=unpack("%32C*",$fucourseid);
2007: my $cusymb=unpack("%32C*",$fusymb);
2008: my $cunique=unpack("%32C*",$perlvar{'lonReceipt'});
2009: return unpack("%32C*",$perlvar{'lonHostID'}).'-'.
2010: ($cunique%$cuname+
2011: $cunique%$cudom+
2012: $cusymb%$cuname+
2013: $cusymb%$cudom+
2014: $cucourseid%$cuname+
2015: $cucourseid%$cudom);
2016: }
2017:
2018: sub receipt {
2019: return &ireceipt($ENV{'user.name'},$ENV{'user.domain'},
2020: $ENV{'request.course.id'},&symbread());
2021: }
2022:
2023: # ------------------------------------------------------------ Serves up a file
2024: # returns either the contents of the file or a -1
2025: sub getfile {
2026: my $file=shift;
2027: &repcopy($file);
2028: if (! -e $file ) { return -1; };
2029: my $fh=Apache::File->new($file);
2030: my $a='';
2031: while (<$fh>) { $a .=$_; }
2032: return $a
2033: }
2034:
2035: sub filelocation {
2036: my ($dir,$file) = @_;
2037: my $location;
2038: $file=~ s/^\s*(\S+)\s*$/$1/; ## strip off leading and trailing spaces
2039: if ($file=~m:^/~:) { # is a contruction space reference
2040: $location = $file;
2041: $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;
2042: } else {
2043: $file=~s/^$perlvar{'lonDocRoot'}//;
2044: $file=~s:^/*res::;
2045: if ( !( $file =~ m:^/:) ) {
2046: $location = $dir. '/'.$file;
2047: } else {
2048: $location = '/home/httpd/html/res'.$file;
2049: }
2050: }
2051: $location=~s://+:/:g; # remove duplicate /
2052: while ($location=~m:/\.\./:) {$location=~ s:/[^/]+/\.\./:/:g;} #remove dir/..
2053: return $location;
2054: }
2055:
2056: sub hreflocation {
2057: my ($dir,$file)=@_;
2058: unless (($_=~/^http:\/\//i) || ($_=~/^\//)) {
2059: my $finalpath=filelocation($dir,$file);
2060: $finalpath=~s/^\/home\/httpd\/html//;
2061: return $finalpath;
2062: } else {
2063: return $file;
2064: }
2065: }
2066:
2067: # ------------------------------------------------------------- Declutters URLs
2068:
2069: sub declutter {
2070: my $thisfn=shift;
2071: $thisfn=~s/^$perlvar{'lonDocRoot'}//;
2072: $thisfn=~s/^\///;
2073: $thisfn=~s/^res\///;
2074: return $thisfn;
2075: }
2076:
2077: # -------------------------------------------------------- Escape Special Chars
2078:
2079: sub escape {
2080: my $str=shift;
2081: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
2082: return $str;
2083: }
2084:
2085: # ----------------------------------------------------- Un-Escape Special Chars
2086:
2087: sub unescape {
2088: my $str=shift;
2089: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
2090: return $str;
2091: }
2092:
2093: # ================================================================ Main Program
2094:
2095: sub BEGIN {
2096: if ($readit ne 'done') {
2097: # ------------------------------------------------------------ Read access.conf
2098: {
2099: my $config=Apache::File->new("/etc/httpd/conf/access.conf");
2100:
2101: while (my $configline=<$config>) {
2102: if ($configline =~ /PerlSetVar/) {
2103: my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
2104: chomp($varvalue);
2105: $perlvar{$varname}=$varvalue;
2106: }
2107: }
2108: }
2109:
2110: # ------------------------------------------------------------- Read hosts file
2111: {
2112: my $config=Apache::File->new("$perlvar{'lonTabDir'}/hosts.tab");
2113:
2114: while (my $configline=<$config>) {
2115: my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
2116: $hostname{$id}=$name;
2117: $hostdom{$id}=$domain;
2118: if ($role eq 'library') { $libserv{$id}=$name; }
2119: }
2120: }
2121:
2122: # ------------------------------------------------------ Read spare server file
2123: {
2124: my $config=Apache::File->new("$perlvar{'lonTabDir'}/spare.tab");
2125:
2126: while (my $configline=<$config>) {
2127: chomp($configline);
2128: if (($configline) && ($configline ne $perlvar{'lonHostID'})) {
2129: $spareid{$configline}=1;
2130: }
2131: }
2132: }
2133: # ------------------------------------------------------------ Read permissions
2134: {
2135: my $config=Apache::File->new("$perlvar{'lonTabDir'}/roles.tab");
2136:
2137: while (my $configline=<$config>) {
2138: chomp($configline);
2139: my ($role,$perm)=split(/ /,$configline);
2140: if ($perm ne '') { $pr{$role}=$perm; }
2141: }
2142: }
2143:
2144: # -------------------------------------------- Read plain texts for permissions
2145: {
2146: my $config=Apache::File->new("$perlvar{'lonTabDir'}/rolesplain.tab");
2147:
2148: while (my $configline=<$config>) {
2149: chomp($configline);
2150: my ($short,$plain)=split(/:/,$configline);
2151: if ($plain ne '') { $prp{$short}=$plain; }
2152: }
2153: }
2154:
2155: # ------------------------------------------------------------- Read file types
2156: {
2157: my $config=Apache::File->new("$perlvar{'lonTabDir'}/filetypes.tab");
2158:
2159: while (my $configline=<$config>) {
2160: chomp($configline);
2161: my ($ending,$emb,@descr)=split(/\s+/,$configline);
2162: if ($descr[0] ne '') {
2163: $fe{$ending}=$emb;
2164: $fd{$ending}=join(' ',@descr);
2165: }
2166: }
2167: }
2168:
2169: %metacache=();
2170:
2171: $readit='done';
2172: &logthis('<font color=yellow>INFO: Read configuration</font>');
2173: }
2174: }
2175: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>