Annotation of loncom/Lond.pm, revision 1.8.2.3.2.5
1.1 droeschl 1: # The LearningOnline Network
2: #
1.8.2.3.2.5! raeburn 3: # $Id: Lond.pm,v 1.8.2.3.2.4 2023/07/05 21:47:51 raeburn Exp $
1.1 droeschl 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: #
9: # LON-CAPA is free software; you can redistribute it and/or modify
10: # it under the terms of the GNU General Public License as published by
11: # the Free Software Foundation; either version 2 of the License, or
12: # (at your option) any later version.
13: #
14: # LON-CAPA is distributed in the hope that it will be useful,
15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: # GNU General Public License for more details.
18: #
19: # You should have received a copy of the GNU General Public License
20: # along with LON-CAPA; if not, write to the Free Software
21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/html/adm/gpl.txt
24: #
25: # http://www.lon-capa.org/
26: #
27: ###
28:
29: #NOTE perldoc at the end of file
1.4 droeschl 30: #TODO move remaining lond functions into this
1.1 droeschl 31:
32: package LONCAPA::Lond;
33:
34: use strict;
35: use lib '/home/httpd/lib/perl/';
36:
37: use LONCAPA;
38: use Apache::lonnet;
39: use GDBM_File;
1.8.2.3.2.1 raeburn 40: use Net::OAuth;
1.8.2.3.2.3 raeburn 41: use Crypt::CBC;
1.8.2.3.2.4 raeburn 42: use Digest::SHA;
43: use Digest::MD5 qw(md5_hex);
1.1 droeschl 44:
45: sub dump_with_regexp {
1.4 droeschl 46: my ( $tail, $clientversion ) = @_;
1.2 droeschl 47: my ( $udom, $uname, $namespace, $regexp, $range ) =
48: split /:/, $tail;
1.1 droeschl 49:
1.4 droeschl 50: $regexp = $regexp ? unescape($regexp) : '.';
1.1 droeschl 51:
52: my ($start,$end);
1.2 droeschl 53:
1.1 droeschl 54: if (defined($range)) {
1.2 droeschl 55: if ($range =~ /^(\d+)\-(\d+)$/) {
56: ($start,$end) = ($1,$2);
57: } elsif ($range =~/^(\d+)$/) {
58: ($start,$end) = (0,$1);
59: } else {
60: undef($range);
61: }
62: }
63:
1.8.2.3.2.5! raeburn 64: #
! 65: # If dump is for file_permissions.db from a pre-2.12 server and
! 66: # $uname:$udom is not a course, determine if value of portaccess
! 67: # in effect for $uname:$udom allows portfolio files to be shared.
! 68: # If sharing is not allowed, records returned for accesscontrol
! 69: # are restricted to those based on ip (i.e., for externalresponse).
! 70: #
! 71: # Note: for 2.12 or later session-hosting server, determination
! 72: # of portaccess value in effect occurs client-side.
! 73: #
! 74: my ($check_portaccess,$access,$now,$major,$minor,%by_ip);
! 75: if ($namespace eq 'file_permissions') {
! 76: if ($clientversion =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
! 77: $major = $1;
! 78: $minor = $2;
! 79: }
! 80: unless ((($major > 2) || (($major == 2) && ($minor > 11))) ||
! 81: &is_course($udom,$uname)) {
! 82: $check_portaccess = 1;
! 83: $access = &portfolio_is_shareable($udom,$uname);
! 84: }
! 85: $now = time;
! 86: }
! 87:
1.2 droeschl 88: my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or
89: return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";
90:
91: my $qresult = '';
92: my $count = 0;
1.1 droeschl 93: #
94: # When dump is for roles.db, determine if LON-CAPA version checking is needed.
1.2 droeschl 95: # Sessions on 2.10 and later do not require version checking, as that occurs
1.1 droeschl 96: # on the server hosting the user session, when constructing the roles/courses
97: # screen).
98: #
1.2 droeschl 99: my $skipcheck;
100: my @ids = &Apache::lonnet::current_machine_ids();
1.8.2.3.2.5! raeburn 101: my %homecourses;
1.1 droeschl 102: #
103: # If dump is for roles.db from a pre-2.10 server, determine the LON-CAPA
1.2 droeschl 104: # version on the server which requested the data.
1.1 droeschl 105: #
1.2 droeschl 106: if ($namespace eq 'roles') {
107: if ($clientversion =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
108: $major = $1;
109: $minor = $2;
1.4 droeschl 110:
1.2 droeschl 111: }
112: if (($major > 2) || (($major == 2) && ($minor > 9))) {
113: $skipcheck = 1;
1.1 droeschl 114: }
1.2 droeschl 115: $now = time;
116: }
117: while (my ($key,$value) = each(%$hashref)) {
118: if ($namespace eq 'roles' && (!$skipcheck)) {
1.1 droeschl 119: if ($key =~ m{^/($LONCAPA::match_domain)/($LONCAPA::match_courseid)(/?[^_]*)_(cc|co|in|ta|ep|ad|st|cr)$}) {
120: my $cdom = $1;
121: my $cnum = $2;
1.2 droeschl 122: my ($role,$roleend,$rolestart) = split(/\_/,$value);
123: if (!$roleend || $roleend > $now) {
1.1 droeschl 124: #
125: # For active course roles, check that requesting server is running a LON-CAPA
126: # version which meets any version requirements for the course. Do not include
127: # the role amongst the results returned if the requesting server's version is
128: # too old.
129: #
130: # This determination is handled differently depending on whether the course's
131: # homeserver is the current server, or whether it is a different server.
132: # In both cases, the course's version requirement needs to be retrieved.
133: #
1.2 droeschl 134: next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
135: $minor,\%homecourses,\@ids));
1.1 droeschl 136: }
137: }
138: }
1.8.2.3.2.5! raeburn 139: if ($namespace eq 'file_permissions') {
! 140: if ($check_portaccess) {
! 141: unless ($access) {
! 142: my $unesckey = &unescape($key);
! 143: if ($unesckey =~ m{\0((\d+)_\d+_\d+:([a-z]+)_(\d+)_(\d+))$}) {
! 144: my ($acl,$timestamp,$scope,$end,$start) = ($1,$2,$3,$4);
! 145: if ($scope eq 'ip') {
! 146: unless (($start > $now) &&
! 147: ($end && $end<$now)) {
! 148: my ($path) = split(/\0/,$unesckey);
! 149: push(@{$by_ip{$path}},{$acl => $timestamp});
! 150: }
! 151: }
! 152: next;
! 153: } elsif ($unesckey =~ m{\0accesscontrol$}) {
! 154: next;
! 155: }
! 156: }
! 157: }
! 158: }
1.2 droeschl 159: if ($regexp eq '.') {
160: $count++;
161: if (defined($range) && $count >= $end) { last; }
162: if (defined($range) && $count < $start) { next; }
163: $qresult.=$key.'='.$value.'&';
164: } else {
165: my $unescapeKey = &unescape($key);
166: if (eval('$unescapeKey=~/$regexp/')) {
167: $count++;
168: if (defined($range) && $count >= $end) { last; }
169: if (defined($range) && $count < $start) { next; }
170: $qresult.="$key=$value&";
171: }
172: }
173: }
1.8.2.3.2.5! raeburn 174: if (($namespace eq 'file_permissions') && ($check_portaccess) && (!$access)) {
! 175: if (keys(%by_ip)) {
! 176: my %accesscontrol;
! 177: foreach my $path (keys(%by_ip)) {
! 178: if (ref($by_ip{$path}) eq 'ARRAY') {
! 179: foreach my $item (@{$by_ip{$path}}) {
! 180: if (ref($item) eq 'HASH') {
! 181: my ($acl,$timestamp) = each(%$item);
! 182: my $key = &escape("$path\0$acl");
! 183: my $value = $hashref->{$key};
! 184: $qresult.= "$key=$value&";
! 185: $accesscontrol{"$path\0accesscontrol"}{$acl} = $timestamp;
! 186: }
! 187: }
! 188: }
! 189: }
! 190: if (keys(%accesscontrol)) {
! 191: while (my ($key,$value) = each(%accesscontrol)) {
! 192: $qresult.= &escape($key).'='.&Apache::lonnet::freeze_escape($value).'&';
! 193: }
! 194: }
! 195: }
! 196: }
1.2 droeschl 197: &untie_user_hash($hashref) or
198: return "error: ".($!+0)." untie(GDBM) Failed while attempting dump";
1.1 droeschl 199: #
200: # If dump is for roles.db from a pre-2.10 server, check if the LON-CAPA
201: # version requirements for courses for which the current server is the home
202: # server permit course roles to be usable on the client server hosting the
203: # user's session. If so, include those role results in the data returned to
204: # the client server.
205: #
1.2 droeschl 206: if (($namespace eq 'roles') && (!$skipcheck)) {
207: if (keys(%homecourses) > 0) {
208: $qresult .= &check_homecourses(\%homecourses,$regexp,$count,
209: $range,$start,$end,$major,$minor);
210: }
211: }
212: chop($qresult);
213: return $qresult;
214: }
215:
216:
217: sub releasereqd_check {
218: my ($cnum,$cdom,$key,$value,$major,$minor,$homecourses,$ids) = @_;
219: my $home = &Apache::lonnet::homeserver($cnum,$cdom);
220: return if ($home eq 'no_host');
221: my ($reqdmajor,$reqdminor,$displayrole);
222: if ($cnum =~ /$LONCAPA::match_community/) {
223: if ($major eq '' && $minor eq '') {
224: return unless ((ref($ids) eq 'ARRAY') &&
225: (grep(/^\Q$home\E$/,@{$ids})));
226: } else {
227: $reqdmajor = 2;
228: $reqdminor = 9;
229: return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
230: }
231: }
232: my $hashid = $cdom.':'.$cnum;
233: my ($courseinfo,$cached) =
234: &Apache::lonnet::is_cached_new('courseinfo',$hashid);
235: if (defined($cached)) {
236: if (ref($courseinfo) eq 'HASH') {
237: if (exists($courseinfo->{'releaserequired'})) {
238: my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
239: return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
240: }
241: }
242: } else {
243: if (ref($ids) eq 'ARRAY') {
244: if (grep(/^\Q$home\E$/,@{$ids})) {
245: if (ref($homecourses) eq 'HASH') {
246: if (ref($homecourses->{$cdom}) eq 'HASH') {
247: if (ref($homecourses->{$cdom}{$cnum}) eq 'HASH') {
248: if (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY') {
249: push(@{$homecourses->{$cdom}{$cnum}},{$key=>$value});
250: } else {
251: $homecourses->{$cdom}{$cnum} = [{$key=>$value}];
252: }
253: } else {
254: $homecourses->{$cdom}{$cnum} = [{$key=>$value}];
255: }
256: } else {
257: $homecourses->{$cdom}{$cnum} = [{$key=>$value}];
258: }
259: }
260: return;
261: }
262: }
263: my $courseinfo = &get_courseinfo_hash($cnum,$cdom,$home);
264: if (ref($courseinfo) eq 'HASH') {
265: if (exists($courseinfo->{'releaserequired'})) {
266: my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
267: return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
268: }
269: } else {
270: return;
271: }
272: }
273: return 1;
274: }
275:
276:
277: sub check_homecourses {
278: my ($homecourses,$regexp,$count,$range,$start,$end,$major,$minor) = @_;
279: my ($result,%addtocache);
280: my $yesterday = time - 24*3600;
281: if (ref($homecourses) eq 'HASH') {
282: my (%okcourses,%courseinfo,%recent);
283: foreach my $domain (keys(%{$homecourses})) {
284: my $hashref =
285: &tie_domain_hash($domain, "nohist_courseids", &GDBM_WRCREAT());
286: if (ref($hashref) eq 'HASH') {
287: while (my ($key,$value) = each(%$hashref)) {
288: my $unesc_key = &unescape($key);
289: if ($unesc_key =~ /^lasttime:(\w+)$/) {
290: my $cid = $1;
291: $cid =~ s/_/:/;
292: if ($value > $yesterday ) {
293: $recent{$cid} = 1;
294: }
295: next;
296: }
297: my $items = &Apache::lonnet::thaw_unescape($value);
298: if (ref($items) eq 'HASH') {
299: my ($cdom,$cnum) = split(/_/,$unesc_key);
300: my $hashid = $cdom.':'.$cnum;
301: $courseinfo{$hashid} = $items;
302: if (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY') {
303: my ($reqdmajor,$reqdminor) = split(/\./,$items->{'releaserequired'});
304: if (&useable_role($reqdmajor,$reqdminor,$major,$minor)) {
305: $okcourses{$hashid} = 1;
306: }
307: }
308: }
309: }
310: unless (&untie_domain_hash($hashref)) {
1.8.2.3 raeburn 311: &Apache::lonnet::logthis("Failed to untie tied hash for nohist_courseids.db for $domain");
1.2 droeschl 312: }
313: } else {
1.8.2.3 raeburn 314: &Apache::lonnet::logthis("Failed to tie hash for nohist_courseids.db for $domain");
1.2 droeschl 315: }
316: }
317: foreach my $hashid (keys(%recent)) {
318: my ($result,$cached)=&Apache::lonnet::is_cached_new('courseinfo',$hashid);
319: unless ($cached) {
320: &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
321: }
322: }
323: foreach my $cdom (keys(%{$homecourses})) {
324: if (ref($homecourses->{$cdom}) eq 'HASH') {
325: foreach my $cnum (keys(%{$homecourses->{$cdom}})) {
326: my $hashid = $cdom.':'.$cnum;
327: next if ($recent{$hashid});
328: &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
329: }
330: }
331: }
332: foreach my $hashid (keys(%okcourses)) {
333: my ($cdom,$cnum) = split(/:/,$hashid);
334: if ((ref($homecourses->{$cdom}) eq 'HASH') &&
335: (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY')) {
336: foreach my $role (@{$homecourses->{$cdom}{$cnum}}) {
337: if (ref($role) eq 'HASH') {
338: while (my ($key,$value) = each(%{$role})) {
339: if ($regexp eq '.') {
340: $count++;
341: if (defined($range) && $count >= $end) { last; }
342: if (defined($range) && $count < $start) { next; }
343: $result.=$key.'='.$value.'&';
344: } else {
345: my $unescapeKey = &unescape($key);
346: if (eval('$unescapeKey=~/$regexp/')) {
347: $count++;
348: if (defined($range) && $count >= $end) { last; }
349: if (defined($range) && $count < $start) { next; }
350: $result.="$key=$value&";
351: }
352: }
353: }
354: }
1.1 droeschl 355: }
356: }
1.2 droeschl 357: }
1.1 droeschl 358: }
1.2 droeschl 359: return $result;
360: }
361:
1.1 droeschl 362:
1.2 droeschl 363: sub useable_role {
364: my ($reqdmajor,$reqdminor,$major,$minor) = @_;
365: if ($reqdmajor ne '' && $reqdminor ne '') {
366: return if (($major eq '' && $minor eq '') ||
367: ($major < $reqdmajor) ||
368: (($major == $reqdmajor) && ($minor < $reqdminor)));
369: }
1.1 droeschl 370: return 1;
371: }
372:
1.2 droeschl 373:
1.3 droeschl 374: sub get_courseinfo_hash {
375: my ($cnum,$cdom,$home) = @_;
376: my %info;
377: eval {
378: local($SIG{ALRM}) = sub { die "timeout\n"; };
379: local($SIG{__DIE__})='DEFAULT';
380: alarm(3);
381: %info = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,1,[$home],'.');
382: alarm(0);
383: };
384: if ($@) {
385: if ($@ eq "timeout\n") {
1.8.2.3 raeburn 386: &Apache::lonnet::logthis("<font color='blue'>WARNING courseiddump for $cnum:$cdom from $home timedout</font>");
1.3 droeschl 387: } else {
1.8.2.3 raeburn 388: &Apache::lonnet::logthis("<font color='yellow'>WARNING unexpected error during eval of call for courseiddump from $home</font>");
1.3 droeschl 389: }
390: } else {
391: if (ref($info{$cdom.'_'.$cnum}) eq 'HASH') {
392: my $hashid = $cdom.':'.$cnum;
393: return &Apache::lonnet::do_cache_new('courseinfo',$hashid,$info{$cdom.'_'.$cnum},600);
394: }
395: }
396: return;
397: }
1.2 droeschl 398:
1.8.2.3.2.5! raeburn 399: sub portfolio_is_shareable {
! 400: my ($udom,$uname) = @_;
! 401: my $check_portaccess = 1;
! 402: my ($userportaccess,$inststatus,$access);
! 403: my $hashref = &tie_user_hash($udom, $uname, 'environment', &GDBM_READER());
! 404: if (ref($hashref) eq 'HASH') {
! 405: my $accesskey = &escape('tools.portaccess');
! 406: $userportaccess = $hashref->{$accesskey};
! 407: $inststatus = $hashref->{'inststatus'};
! 408: &untie_user_hash($hashref);
! 409: }
! 410: if ($userportaccess ne '') {
! 411: $access = $userportaccess;
! 412: } else {
! 413: my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
! 414: if (ref($domdefs{'portaccess'}) eq 'HASH') {
! 415: if (($domdefs{'portaccess'}{'_LC_adv'} ne '') &&
! 416: (&Apache::lonnet::is_advanced_user($udom,$uname))) {
! 417: if ($domdefs{'portaccess'}{'_LC_adv'}) {
! 418: $access = 1;
! 419: } else {
! 420: $access = 0;
! 421: }
! 422: } elsif ($inststatus ne '') {
! 423: my ($hasaccess,$hasnoaccess);
! 424: foreach my $affiliation (split(/:/,$inststatus)) {
! 425: if ($domdefs{'portaccess'}{$affiliation} ne '') {
! 426: if ($domdefs{'portaccess'}{$affiliation}) {
! 427: $hasaccess = 1;
! 428: } else {
! 429: $hasnoaccess = 1;
! 430: }
! 431: }
! 432: }
! 433: if ($hasaccess || $hasnoaccess) {
! 434: if ($hasaccess) {
! 435: $access = 1;
! 436: } elsif ($hasnoaccess) {
! 437: $access = 0;
! 438: }
! 439: }
! 440: } else {
! 441: if ($domdefs{'portaccess'}{'default'} ne '') {
! 442: if ($domdefs{'portaccess'}{'default'}) {
! 443: $access = 1;
! 444: } elsif ($domdefs{'portaccess'}{'default'} == 0) {
! 445: $access = 0;
! 446: }
! 447: }
! 448: }
! 449: } else {
! 450: $access = 1;
! 451: }
! 452: }
! 453: return $access;
! 454: }
! 455:
1.4 droeschl 456: sub dump_course_id_handler {
457: my ($tail) = @_;
458:
459: my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter,
460: $typefilter,$regexp_ok,$rtn_as_hash,$selfenrollonly,$catfilter,$showhidden,
461: $caller,$cloner,$cc_clone_list,$cloneonly,$createdbefore,$createdafter,
1.7 raeburn 462: $creationcontext,$domcloner,$hasuniquecode,$reqcrsdom,$reqinstcode) = split(/:/,$tail);
1.4 droeschl 463: my $now = time;
464: my ($cloneruname,$clonerudom,%cc_clone);
465: if (defined($description)) {
466: $description=&unescape($description);
467: } else {
468: $description='.';
469: }
470: if (defined($instcodefilter)) {
471: $instcodefilter=&unescape($instcodefilter);
472: } else {
473: $instcodefilter='.';
474: }
475: my ($ownerunamefilter,$ownerdomfilter);
476: if (defined($ownerfilter)) {
477: $ownerfilter=&unescape($ownerfilter);
478: if ($ownerfilter ne '.' && defined($ownerfilter)) {
479: if ($ownerfilter =~ /^([^:]*):([^:]*)$/) {
480: $ownerunamefilter = $1;
481: $ownerdomfilter = $2;
482: } else {
483: $ownerunamefilter = $ownerfilter;
484: $ownerdomfilter = '';
485: }
486: }
487: } else {
488: $ownerfilter='.';
489: }
490:
491: if (defined($coursefilter)) {
492: $coursefilter=&unescape($coursefilter);
493: } else {
494: $coursefilter='.';
495: }
496: if (defined($typefilter)) {
497: $typefilter=&unescape($typefilter);
498: } else {
499: $typefilter='.';
500: }
501: if (defined($regexp_ok)) {
502: $regexp_ok=&unescape($regexp_ok);
503: }
504: if (defined($catfilter)) {
505: $catfilter=&unescape($catfilter);
506: }
507: if (defined($cloner)) {
508: $cloner = &unescape($cloner);
509: ($cloneruname,$clonerudom) = ($cloner =~ /^($LONCAPA::match_username):($LONCAPA::match_domain)$/);
510: }
511: if (defined($cc_clone_list)) {
512: $cc_clone_list = &unescape($cc_clone_list);
513: my @cc_cloners = split('&',$cc_clone_list);
514: foreach my $cid (@cc_cloners) {
515: my ($clonedom,$clonenum) = split(':',$cid);
516: next if ($clonedom ne $udom);
517: $cc_clone{$clonedom.'_'.$clonenum} = 1;
518: }
519: }
520: if ($createdbefore ne '') {
521: $createdbefore = &unescape($createdbefore);
522: } else {
523: $createdbefore = 0;
524: }
525: if ($createdafter ne '') {
526: $createdafter = &unescape($createdafter);
527: } else {
528: $createdafter = 0;
529: }
530: if ($creationcontext ne '') {
531: $creationcontext = &unescape($creationcontext);
532: } else {
533: $creationcontext = '.';
534: }
1.6 raeburn 535: unless ($hasuniquecode) {
536: $hasuniquecode = '.';
537: }
1.8 raeburn 538: if ($reqinstcode ne '') {
539: $reqinstcode = &unescape($reqinstcode);
540: }
1.4 droeschl 541: my $unpack = 1;
542: if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' &&
543: $typefilter eq '.') {
544: $unpack = 0;
545: }
546: if (!defined($since)) { $since=0; }
1.7 raeburn 547: my (%gotcodedefaults,%otcodedefaults);
1.4 droeschl 548: my $qresult='';
549:
550: my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT())
551: or return "error: ".($!+0)." tie(GDBM) Failed while attempting courseiddump";
552:
553: while (my ($key,$value) = each(%$hashref)) {
554: my ($unesc_key,$lasttime_key,$lasttime,$is_hash,%val,
555: %unesc_val,$selfenroll_end,$selfenroll_types,$created,
556: $context);
557: $unesc_key = &unescape($key);
558: if ($unesc_key =~ /^lasttime:/) {
559: next;
560: } else {
561: $lasttime_key = &escape('lasttime:'.$unesc_key);
562: }
563: if ($hashref->{$lasttime_key} ne '') {
564: $lasttime = $hashref->{$lasttime_key};
565: next if ($lasttime<$since);
566: }
1.7 raeburn 567: my ($canclone,$valchange,$clonefromcode);
1.4 droeschl 568: my $items = &Apache::lonnet::thaw_unescape($value);
569: if (ref($items) eq 'HASH') {
570: if ($hashref->{$lasttime_key} eq '') {
571: next if ($since > 1);
572: }
1.7 raeburn 573: if ($items->{'inst_code'}) {
574: $clonefromcode = $items->{'inst_code'};
575: }
1.4 droeschl 576: $is_hash = 1;
577: if ($domcloner) {
578: $canclone = 1;
579: } elsif (defined($clonerudom)) {
580: if ($items->{'cloners'}) {
581: my @cloneable = split(',',$items->{'cloners'});
582: if (@cloneable) {
583: if (grep(/^\*$/,@cloneable)) {
584: $canclone = 1;
585: } elsif (grep(/^\*:\Q$clonerudom\E$/,@cloneable)) {
586: $canclone = 1;
587: } elsif (grep(/^\Q$cloneruname\E:\Q$clonerudom\E$/,@cloneable)) {
588: $canclone = 1;
589: }
590: }
591: unless ($canclone) {
592: if ($cloneruname ne '' && $clonerudom ne '') {
593: if ($cc_clone{$unesc_key}) {
594: $canclone = 1;
595: $items->{'cloners'} .= ','.$cloneruname.':'.
596: $clonerudom;
597: $valchange = 1;
598: }
599: }
600: }
1.7 raeburn 601: unless ($canclone) {
602: if (($reqcrsdom eq $udom) && ($reqinstcode) && ($clonefromcode)) {
603: if (grep(/\=/,@cloneable)) {
604: foreach my $cloner (@cloneable) {
605: if (($cloner ne '*') && ($cloner !~ /^\*\:$LONCAPA::match_domain$/) &&
606: ($cloner !~ /^$LONCAPA::match_username\:$LONCAPA::match_domain$/) && ($cloner ne '')) {
607: if ($cloner =~ /=/) {
608: my (%codedefaults,@code_order);
609: if (ref($gotcodedefaults{$udom}) eq 'HASH') {
610: if (ref($gotcodedefaults{$udom}{'defaults'}) eq 'HASH') {
611: %codedefaults = %{$gotcodedefaults{$udom}{'defaults'}};
612: }
613: if (ref($gotcodedefaults{$udom}{'order'}) eq 'ARRAY') {
614: @code_order = @{$gotcodedefaults{$udom}{'order'}};
615: }
616: } else {
617: &Apache::lonnet::auto_instcode_defaults($udom,
618: \%codedefaults,
619: \@code_order);
620: $gotcodedefaults{$udom}{'defaults'} = \%codedefaults;
621: $gotcodedefaults{$udom}{'order'} = \@code_order;
622: }
623: if (@code_order > 0) {
624: if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
625: $cloner,$clonefromcode,$reqinstcode)) {
626: $canclone = 1;
627: last;
628: }
629: }
630: }
631: }
632: }
633: }
634: }
635: }
1.4 droeschl 636: } elsif (defined($cloneruname)) {
637: if ($cc_clone{$unesc_key}) {
638: $canclone = 1;
639: $items->{'cloners'} = $cloneruname.':'.$clonerudom;
640: $valchange = 1;
641: }
642: unless ($canclone) {
643: if ($items->{'owner'} =~ /:/) {
644: if ($items->{'owner'} eq $cloner) {
645: $canclone = 1;
646: }
647: } elsif ($cloner eq $items->{'owner'}.':'.$udom) {
648: $canclone = 1;
649: }
650: if ($canclone) {
651: $items->{'cloners'} = $cloneruname.':'.$clonerudom;
652: $valchange = 1;
653: }
654: }
655: }
1.7 raeburn 656: unless (($canclone) || ($items->{'cloners'})) {
657: my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
658: if ($domdefs{'canclone'}) {
659: unless ($domdefs{'canclone'} eq 'none') {
660: if ($domdefs{'canclone'} eq 'domain') {
661: if ($clonerudom eq $udom) {
662: $canclone = 1;
663: }
664: } elsif (($clonefromcode) && ($reqinstcode) &&
665: ($udom eq $reqcrsdom)) {
666: if (&Apache::lonnet::default_instcode_cloning($udom,$domdefs{'canclone'},
667: $clonefromcode,$reqinstcode)) {
668: $canclone = 1;
669: }
670: }
671: }
672: }
673: }
1.4 droeschl 674: }
675: if ($unpack || !$rtn_as_hash) {
676: $unesc_val{'descr'} = $items->{'description'};
677: $unesc_val{'inst_code'} = $items->{'inst_code'};
678: $unesc_val{'owner'} = $items->{'owner'};
679: $unesc_val{'type'} = $items->{'type'};
680: $unesc_val{'cloners'} = $items->{'cloners'};
681: $unesc_val{'created'} = $items->{'created'};
682: $unesc_val{'context'} = $items->{'context'};
683: }
684: $selfenroll_types = $items->{'selfenroll_types'};
685: $selfenroll_end = $items->{'selfenroll_end_date'};
686: $created = $items->{'created'};
687: $context = $items->{'context'};
688: if ($selfenrollonly) {
689: next if (!$selfenroll_types);
690: if (($selfenroll_end > 0) && ($selfenroll_end <= $now)) {
691: next;
692: }
693: }
694: if ($creationcontext ne '.') {
695: next if (($context ne '') && ($context ne $creationcontext));
696: }
697: if ($createdbefore > 0) {
698: next if (($created eq '') || ($created > $createdbefore));
699: }
700: if ($createdafter > 0) {
701: next if (($created eq '') || ($created <= $createdafter));
702: }
703: if ($catfilter ne '') {
704: next if ($items->{'categories'} eq '');
705: my @categories = split('&',$items->{'categories'});
706: next if (@categories == 0);
707: my @subcats = split('&',$catfilter);
708: my $matchcat = 0;
709: foreach my $cat (@categories) {
710: if (grep(/^\Q$cat\E$/,@subcats)) {
711: $matchcat = 1;
712: last;
713: }
714: }
715: next if (!$matchcat);
716: }
717: if ($caller eq 'coursecatalog') {
718: if ($items->{'hidefromcat'} eq 'yes') {
719: next if !$showhidden;
720: }
721: }
1.6 raeburn 722: if ($hasuniquecode ne '.') {
723: next unless ($items->{'uniquecode'});
724: }
1.4 droeschl 725: } else {
726: next if ($catfilter ne '');
727: next if ($selfenrollonly);
728: next if ($createdbefore || $createdafter);
729: next if ($creationcontext ne '.');
730: if ((defined($clonerudom)) && (defined($cloneruname))) {
731: if ($cc_clone{$unesc_key}) {
732: $canclone = 1;
733: $val{'cloners'} = &escape($cloneruname.':'.$clonerudom);
734: }
735: }
736: $is_hash = 0;
737: my @courseitems = split(/:/,$value);
738: $lasttime = pop(@courseitems);
739: if ($hashref->{$lasttime_key} eq '') {
740: next if ($lasttime<$since);
741: }
742: ($val{'descr'},$val{'inst_code'},$val{'owner'},$val{'type'}) = @courseitems;
743: }
744: if ($cloneonly) {
745: next unless ($canclone);
746: }
747: my $match = 1;
748: if ($description ne '.') {
749: if (!$is_hash) {
750: $unesc_val{'descr'} = &unescape($val{'descr'});
751: }
752: if (eval{$unesc_val{'descr'} !~ /\Q$description\E/i}) {
753: $match = 0;
754: }
755: }
756: if ($instcodefilter ne '.') {
757: if (!$is_hash) {
758: $unesc_val{'inst_code'} = &unescape($val{'inst_code'});
759: }
760: if ($regexp_ok == 1) {
761: if (eval{$unesc_val{'inst_code'} !~ /$instcodefilter/}) {
762: $match = 0;
763: }
764: } elsif ($regexp_ok == -1) {
765: if (eval{$unesc_val{'inst_code'} =~ /$instcodefilter/}) {
766: $match = 0;
767: }
768: } else {
769: if (eval{$unesc_val{'inst_code'} !~ /\Q$instcodefilter\E/i}) {
770: $match = 0;
771: }
772: }
773: }
774: if ($ownerfilter ne '.') {
775: if (!$is_hash) {
776: $unesc_val{'owner'} = &unescape($val{'owner'});
777: }
778: if (($ownerunamefilter ne '') && ($ownerdomfilter ne '')) {
779: if ($unesc_val{'owner'} =~ /:/) {
780: if (eval{$unesc_val{'owner'} !~
781: /\Q$ownerunamefilter\E:\Q$ownerdomfilter\E$/i}) {
782: $match = 0;
783: }
784: } else {
785: if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
786: $match = 0;
787: }
788: }
789: } elsif ($ownerunamefilter ne '') {
790: if ($unesc_val{'owner'} =~ /:/) {
791: if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E:[^:]+$/i}) {
792: $match = 0;
793: }
794: } else {
795: if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
796: $match = 0;
797: }
798: }
799: } elsif ($ownerdomfilter ne '') {
800: if ($unesc_val{'owner'} =~ /:/) {
801: if (eval{$unesc_val{'owner'} !~ /^[^:]+:\Q$ownerdomfilter\E/}) {
802: $match = 0;
803: }
804: } else {
805: if ($ownerdomfilter ne $udom) {
806: $match = 0;
807: }
808: }
809: }
810: }
811: if ($coursefilter ne '.') {
812: if (eval{$unesc_key !~ /^$udom(_)\Q$coursefilter\E$/}) {
813: $match = 0;
814: }
815: }
816: if ($typefilter ne '.') {
817: if (!$is_hash) {
818: $unesc_val{'type'} = &unescape($val{'type'});
819: }
820: if ($unesc_val{'type'} eq '') {
821: if ($typefilter ne 'Course') {
822: $match = 0;
823: }
824: } else {
825: if (eval{$unesc_val{'type'} !~ /^\Q$typefilter\E$/}) {
826: $match = 0;
827: }
828: }
829: }
830: if ($match == 1) {
831: if ($rtn_as_hash) {
832: if ($is_hash) {
833: if ($valchange) {
834: my $newvalue = &Apache::lonnet::freeze_escape($items);
835: $qresult.=$key.'='.$newvalue.'&';
836: } else {
837: $qresult.=$key.'='.$value.'&';
838: }
839: } else {
840: my %rtnhash = ( 'description' => &unescape($val{'descr'}),
841: 'inst_code' => &unescape($val{'inst_code'}),
842: 'owner' => &unescape($val{'owner'}),
843: 'type' => &unescape($val{'type'}),
844: 'cloners' => &unescape($val{'cloners'}),
845: );
846: my $items = &Apache::lonnet::freeze_escape(\%rtnhash);
847: $qresult.=$key.'='.$items.'&';
848: }
849: } else {
850: if ($is_hash) {
851: $qresult .= $key.'='.&escape($unesc_val{'descr'}).':'.
852: &escape($unesc_val{'inst_code'}).':'.
853: &escape($unesc_val{'owner'}).'&';
854: } else {
855: $qresult .= $key.'='.$val{'descr'}.':'.$val{'inst_code'}.
856: ':'.$val{'owner'}.'&';
857: }
858: }
859: }
860: }
861: &untie_domain_hash($hashref) or
862: return "error: ".($!+0)." untie(GDBM) Failed while attempting courseiddump";
863:
864: chop($qresult);
865: return $qresult;
866: }
867:
868: sub dump_profile_database {
869: my ($tail) = @_;
870:
871: my ($udom,$uname,$namespace) = split(/:/,$tail);
872:
873: my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or
874: return "error: ".($!+0)." tie(GDBM) Failed while attempting currentdump";
875:
876: # Structure of %data:
877: # $data{$symb}->{$parameter}=$value;
878: # $data{$symb}->{'v.'.$parameter}=$version;
879: # since $parameter will be unescaped, we do not
880: # have to worry about silly parameter names...
881:
882: my $qresult='';
883: my %data = (); # A hash of anonymous hashes..
884: while (my ($key,$value) = each(%$hashref)) {
885: my ($v,$symb,$param) = split(/:/,$key);
886: next if ($v eq 'version' || $symb eq 'keys');
887: next if (exists($data{$symb}) &&
888: exists($data{$symb}->{$param}) &&
889: $data{$symb}->{'v.'.$param} > $v);
890: $data{$symb}->{$param}=$value;
891: $data{$symb}->{'v.'.$param}=$v;
892: }
893:
894: &untie_user_hash($hashref) or
895: return "error: ".($!+0)." untie(GDBM) Failed while attempting currentdump";
896:
897: while (my ($symb,$param_hash) = each(%data)) {
898: while(my ($param,$value) = each (%$param_hash)){
899: next if ($param =~ /^v\./); # Ignore versions...
900: #
901: # Just dump the symb=value pairs separated by &
902: #
903: $qresult.=$symb.':'.$param.'='.$value.'&';
904: }
905: }
1.2 droeschl 906:
1.4 droeschl 907: chop($qresult);
908: return $qresult;
909: }
1.2 droeschl 910:
1.8.2.1 raeburn 911: sub is_course {
912: my ($cdom,$cnum) = @_;
913:
914: return unless (($cdom =~ /^$LONCAPA::match_domain$/) &&
915: ($cnum =~ /^$LONCAPA::match_courseid$/));
916: my $hashid = $cdom.':'.$cnum;
917: my ($iscourse,$cached) =
918: &Apache::lonnet::is_cached_new('iscourse',$hashid);
919: unless (defined($cached)) {
920: my $hashref =
921: &tie_domain_hash($cdom, "nohist_courseids", &GDBM_WRCREAT());
922: if (ref($hashref) eq 'HASH') {
923: my $esc_key = &escape($cdom.'_'.$cnum);
924: if (exists($hashref->{$esc_key})) {
925: $iscourse = 1;
926: } else {
927: $iscourse = 0;
928: }
929: &Apache::lonnet::do_cache_new('iscourse',$hashid,$iscourse,3600);
930: unless (&untie_domain_hash($hashref)) {
1.8.2.3 raeburn 931: &Apache::lonnet::logthis("Failed to untie tied hash for nohist_courseids.db for $cdom");
1.8.2.1 raeburn 932: }
933: } else {
1.8.2.3 raeburn 934: &Apache::lonnet::logthis("Failed to tie hash for nohist_courseids.db for $cdom");
1.8.2.1 raeburn 935: }
936: }
937: return $iscourse;
938: }
1.2 droeschl 939:
1.8.2.2 raeburn 940: sub get_dom {
941: my ($userinput) = @_;
942: my ($cmd,$udom,$namespace,$what) =split(/:/,$userinput,4);
943: my $hashref = &tie_domain_hash($udom,$namespace,&GDBM_READER()) or
944: return "error: ".($!+0)." tie(GDBM) Failed while attempting $cmd";
945: my $qresult='';
946: if (ref($hashref)) {
947: chomp($what);
948: my @queries=split(/\&/,$what);
949: for (my $i=0;$i<=$#queries;$i++) {
950: $qresult.="$hashref->{$queries[$i]}&";
951: }
952: $qresult=~s/\&$//;
953: }
954: &untie_user_hash($hashref) or
955: return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd";
956: return $qresult;
957: }
958:
1.8.2.3.2.3 raeburn 959: sub store_dom {
960: my ($userinput) = @_;
961: my ($cmd,$dom,$namespace,$rid,$what) =split(/:/,$userinput);
962: my $hashref = &tie_domain_hash($dom,$namespace,&GDBM_WRCREAT(),"S","$rid:$what") or
963: return "error: ".($!+0)." tie(GDBM) Failed while attempting $cmd";
964: $hashref->{"version:$rid"}++;
965: my $version=$hashref->{"version:$rid"};
966: my $allkeys='';
967: my @pairs=split(/\&/,$what);
968: foreach my $pair (@pairs) {
969: my ($key,$value)=split(/=/,$pair);
970: $allkeys.=$key.':';
971: $hashref->{"$version:$rid:$key"}=$value;
972: }
973: my $now = time;
974: $hashref->{"$version:$rid:timestamp"}=$now;
975: $allkeys.='timestamp';
976: $hashref->{"$version:keys:$rid"}=$allkeys;
977: &untie_user_hash($hashref) or
978: return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd";
979: return 'ok';
980: }
981:
982: sub restore_dom {
983: my ($userinput) = @_;
984: my ($cmd,$dom,$namespace,$rid) = split(/:/,$userinput);
985: my $hashref = &tie_domain_hash($dom,$namespace,&GDBM_READER()) or
986: return "error: ".($!+0)." tie(GDBM) Failed while attempting $cmd";
987: my $qresult='';
988: if (ref($hashref)) {
989: chomp($rid);
990: my $version=$hashref->{"version:$rid"};
991: $qresult.="version=$version&";
992: my $scope;
993: for ($scope=1;$scope<=$version;$scope++) {
994: my $vkeys=$hashref->{"$scope:keys:$rid"};
995: my @keys=split(/:/,$vkeys);
996: my $key;
997: $qresult.="$scope:keys=$vkeys&";
998: foreach $key (@keys) {
999: $qresult.="$scope:$key=".$hashref->{"$scope:$rid:$key"}."&";
1000: }
1001: }
1002: $qresult=~s/\&$//;
1003: }
1004: &untie_user_hash($hashref) or
1005: return "error: ".($!+0)." untie(GDBM) Failed while attempting $cmd";
1006: return $qresult;
1007: }
1008:
1.8.2.3.2.1 raeburn 1009: sub crslti_itemid {
1010: my ($cdom,$cnum,$url,$method,$params,$loncaparev) = @_;
1011: unless (ref($params) eq 'HASH') {
1012: return;
1013: }
1014: if (($cdom eq '') || ($cnum eq '')) {
1015: return;
1016: }
1017: my ($itemid,$consumer_key,$secret);
1018:
1019: if (exists($params->{'oauth_callback'})) {
1020: $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
1021: } else {
1022: $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
1023: }
1024:
1025: my $consumer_key = $params->{'oauth_consumer_key'};
1026: return if ($consumer_key eq '');
1027:
1028: my (%crslti,%crslti_by_key);
1029: my $hashid=$cdom.'_'.$cnum;
1030: my ($result,$cached)=&Apache::lonnet::is_cached_new('courseltienc',$hashid);
1031: if (defined($cached)) {
1032: if (ref($result) eq 'HASH') {
1033: %crslti = %{$result};
1034: }
1035: } else {
1036: my $reply = &dump_with_regexp(join(":",($cdom,$cnum,'nohist_ltienc','','')),$loncaparev);
1037: %crslti = %{&Apache::lonnet::unserialize($reply)};
1038: my $cachetime = 24*60*60;
1039: &Apache::lonnet::do_cache_new('courseltienc',$hashid,\%crslti,$cachetime);
1040: }
1041:
1042: return if (!keys(%crslti));
1043:
1044: foreach my $id (keys(%crslti)) {
1045: if (ref($crslti{$id}) eq 'HASH') {
1046: my $key = $crslti{$id}{'key'};
1047: if (($key ne '') && ($crslti{$id}{'secret'} ne '')) {
1048: push(@{$crslti_by_key{$key}},$id);
1049: }
1050: }
1051: }
1052:
1053: return if (!keys(%crslti_by_key));
1054:
1.8.2.3.2.3 raeburn 1055: my %courselti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
1056:
1.8.2.3.2.1 raeburn 1057: if (ref($crslti_by_key{$consumer_key}) eq 'ARRAY') {
1058: foreach my $id (@{$crslti_by_key{$consumer_key}}) {
1059: my $secret = $crslti{$id}{'secret'};
1.8.2.3.2.3 raeburn 1060: if (ref($courselti{$id}) eq 'HASH') {
1061: if ((exists($courselti{$id}{'cipher'})) &&
1062: ($courselti{$id}{'cipher'} =~ /^\d+$/)) {
1063: my $keynum = $courselti{$id}{'cipher'};
1064: my $privkey = &get_dom("getdom:$cdom:private:$keynum:lti:key");
1065: if ($privkey ne '') {
1066: my $cipher = new Crypt::CBC($privkey);
1067: $secret = $cipher->decrypt_hex($secret);
1068: }
1069: }
1070: }
1.8.2.3.2.1 raeburn 1071: my $request = Net::OAuth->request('request token')->from_hash($params,
1072: request_url => $url,
1073: request_method => $method,
1074: consumer_secret => $secret,);
1075: if ($request->verify()) {
1076: $itemid = $id;
1077: last;
1078: }
1079: }
1080: }
1081: return $itemid;
1082: }
1083:
1084: sub domlti_itemid {
1085: my ($dom,$context,$url,$method,$params,$loncaparev) = @_;
1086: unless (ref($params) eq 'HASH') {
1087: return;
1088: }
1089: if ($dom eq '') {
1090: return;
1091: }
1092: my ($itemid,$consumer_key,$secret);
1093:
1094: if (exists($params->{'oauth_callback'})) {
1095: $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
1096: } else {
1097: $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
1098: }
1099:
1100: my $consumer_key = $params->{'oauth_consumer_key'};
1101: return if ($consumer_key eq '');
1102:
1.8.2.3.2.3 raeburn 1103: my ($name,$cachename);
1104: if ($context eq 'linkprot') {
1105: $name = $context;
1106: } else {
1107: $name = 'lti';
1.8.2.3.2.4 raeburn 1108: if ($context eq '') {
1109: $context = 'provider';
1110: }
1.8.2.3.2.3 raeburn 1111: }
1112: $cachename = $name.'enc';
1.8.2.3.2.1 raeburn 1113: my %ltienc;
1.8.2.3.2.3 raeburn 1114: my ($encresult,$enccached)=&Apache::lonnet::is_cached_new($cachename,$dom);
1.8.2.3.2.1 raeburn 1115: if (defined($enccached)) {
1116: if (ref($encresult) eq 'HASH') {
1117: %ltienc = %{$encresult};
1118: }
1119: } else {
1.8.2.3.2.3 raeburn 1120: my $reply = &get_dom("getdom:$dom:encconfig:$name");
1.8.2.3.2.1 raeburn 1121: my $ltiencref = &Apache::lonnet::thaw_unescape($reply);
1122: if (ref($ltiencref) eq 'HASH') {
1123: %ltienc = %{$ltiencref};
1124: }
1125: my $cachetime = 24*60*60;
1.8.2.3.2.3 raeburn 1126: &Apache::lonnet::do_cache_new($cachename,$dom,\%ltienc,$cachetime);
1.8.2.3.2.1 raeburn 1127: }
1128:
1129: return if (!keys(%ltienc));
1130:
1131: my %lti_by_key;
1132: foreach my $id (keys(%ltienc)) {
1133: if (ref($ltienc{$id}) eq 'HASH') {
1134: my $key = $ltienc{$id}{'key'};
1135: if (($key ne '') && ($ltienc{$id}{'secret'} ne '')) {
1.8.2.3.2.3 raeburn 1136: push(@{$lti_by_key{$key}},$id);
1.8.2.3.2.1 raeburn 1137: }
1138: }
1139: }
1140: return if (!keys(%lti_by_key));
1141:
1.8.2.3.2.3 raeburn 1142: my %lti = &Apache::lonnet::get_domain_lti($dom,$context);
1143:
1.8.2.3.2.1 raeburn 1144: if (ref($lti_by_key{$consumer_key}) eq 'ARRAY') {
1145: foreach my $id (@{$lti_by_key{$consumer_key}}) {
1146: my $secret = $ltienc{$id}{'secret'};
1.8.2.3.2.3 raeburn 1147: if (ref($lti{$id}) eq 'HASH') {
1148: if ((exists($lti{$id}{'cipher'})) &&
1149: ($lti{$id}{'cipher'} =~ /^\d+$/)) {
1150: my $keynum = $lti{$id}{'cipher'};
1151: my $privkey = &get_dom("getdom:$dom:private:$keynum:lti:key");
1152: if ($privkey ne '') {
1153: my $cipher = new Crypt::CBC($privkey);
1154: $secret = $cipher->decrypt_hex($secret);
1155: }
1156: }
1157: }
1.8.2.3.2.1 raeburn 1158: my $request = Net::OAuth->request('request token')->from_hash($params,
1159: request_url => $url,
1160: request_method => $method,
1161: consumer_secret => $secret,);
1162: if ($request->verify()) {
1163: $itemid = $id;
1164: last;
1165: }
1166: }
1167: }
1168: return $itemid;
1169: }
1170:
1.8.2.3.2.4 raeburn 1171: sub sign_lti_payload {
1172: my ($cdom,$cnum,$crsdef,$type,$context,$url,$idx,$keynum,$loncaparev,$paramsref,$inforef) = @_;
1173: return unless (ref($paramsref) eq 'HASH');
1174: my ($sigmethod,$callback,$reqtype,$reqmethod,$respfmt,$bodyhash);
1175: if (ref($inforef) eq 'HASH') {
1176: if (exists($inforef->{'method'})) {
1177: $sigmethod = $inforef->{'method'};
1178: }
1179: if (exists($inforef->{'cb'})) {
1180: $callback = $inforef->{'cb'};
1181: }
1182: if (exists($inforef->{'reqtype'})) {
1183: $reqtype = $inforef->{'reqtype'};
1184: }
1185: if (exists($inforef->{'reqmethod'})) {
1186: $reqmethod = $inforef->{'reqmethod'};
1187: }
1188: if (exists($inforef->{'body_hash'})) {
1189: $bodyhash = $inforef->{'body_hash'};
1190: }
1191: if (exists($inforef->{'respfmt'})) {
1192: $respfmt = $inforef->{'respfmt'};
1193: }
1194: }
1195: my ($key,$secret) = &get_lti_credentials($cdom,$cnum,$crsdef,$type,$idx,$keynum,$loncaparev);
1196: return if (($key eq '') || ($secret eq ''));
1197: if ($sigmethod eq '') {
1198: $sigmethod = 'HMAC-SHA1';
1199: }
1200: if ($callback eq '') {
1201: $callback = 'about:blank',
1202: }
1203: if ($reqtype eq '') {
1204: $reqtype = 'request token';
1205: }
1206: if ($reqmethod eq '') {
1207: $reqmethod = 'POST';
1208: }
1209: srand( time() ^ ($$ + ($$ << 15)) ); # Seed rand.
1210: my $nonce = Digest::SHA::sha1_hex(sprintf("%06x%06x",rand(0xfffff0),rand(0xfffff0)));
1211: my $request;
1212: if (($context eq 'grade') && ($reqtype eq 'consumer') && ($bodyhash ne '')) {
1213: $request = Net::OAuth->request($reqtype)->new(
1214: consumer_key => $key,
1215: consumer_secret => $secret,
1216: request_url => $url,
1217: request_method => $reqmethod,
1218: signature_method => $sigmethod,
1219: timestamp => time(),
1220: nonce => $nonce,
1221: body_hash => $bodyhash,
1222: );
1223: $request->add_required_message_params('body_hash');
1224: } else {
1225: $request = Net::OAuth->request($reqtype)->new(
1226: consumer_key => $key,
1227: consumer_secret => $secret,
1228: request_url => $url,
1229: request_method => 'POST',
1230: signature_method => $sigmethod,
1231: timestamp => time,
1232: nonce => $nonce,
1233: callback => $callback,
1234: extra_params => $paramsref,
1235: version => '1.0',
1236: );
1237: }
1238: $request->sign();
1239: if ($respfmt eq 'to_post_body') {
1240: return $request->to_post_body();
1241: } elsif ($respfmt eq 'to_authorization_header') {
1242: return $request->to_authorization_header();
1243: } else {
1244: return $request->to_hash();
1245: }
1246: }
1247:
1248: sub get_lti_credentials {
1249: my ($cdom,$cnum,$crsdef,$type,$idx,$keynum,$loncaparev) = @_;
1250: my ($dbname,$name,$cachename,$hashid,$key,$secret,%ltienc);
1251: if ($crsdef) {
1252: $hashid = $cdom.'_'.$cnum;
1253: } else {
1254: $hashid = $cdom;
1255: }
1256: if ($type eq 'tools') {
1257: if ($crsdef) {
1258: $dbname = 'nohist_toolsenc';
1259: $cachename = 'crsltitoolsenc';
1260: } else {
1261: $name = 'ltitools';
1262: $dbname = 'encconfig';
1263: $cachename = 'ltitoolsenc';
1264: }
1265: } elsif ($type eq 'linkprot') {
1266: if ($crsdef) {
1267: $dbname = 'nohist_ltienc';
1268: $cachename = 'courseltienc';
1269: } else {
1270: $name = 'linkprot';
1271: $dbname = 'encconfig';
1272: $cachename = 'linkprotenc';
1273: }
1274: } elsif ($type eq 'lti') {
1275: $name = 'lti';
1276: $dbname = 'encconfig';
1277: $cachename = 'ltienc';
1278: }
1279: my ($encresult,$enccached)=&Apache::lonnet::is_cached_new($cachename,$hashid);
1280: if (defined($enccached)) {
1281: if (ref($encresult) eq 'HASH') {
1282: %ltienc = %{$encresult};
1283: }
1284: } else {
1285: if ($crsdef) {
1286: my $reply = &dump_with_regexp(join(":",($cdom,$cnum,$dbname,'','')),$loncaparev);
1287: %ltienc = %{&Apache::lonnet::unserialize($reply)};
1288: } else {
1289: my $reply = &get_dom("getdom:$cdom:$dbname:$name");
1290: my $encref = &Apache::lonnet::thaw_unescape($reply);
1291: if (ref($encref) eq 'HASH') {
1292: %ltienc = %{$encref};
1293: }
1294: }
1295: my $cachetime = 24*60*60;
1296: &Apache::lonnet::do_cache_new($cachename,$hashid,\%ltienc,$cachetime);
1297: }
1298: if (!keys(%ltienc)) {
1299: return ();
1300: } elsif (exists($ltienc{$idx})) {
1301: if (ref($ltienc{$idx}) eq 'HASH') {
1302: if (exists($ltienc{$idx}{'key'})) {
1303: $key = $ltienc{$idx}{'key'};
1304: }
1305: if (exists($ltienc{$idx}{'secret'})) {
1306: $secret = $ltienc{$idx}{'secret'};
1307: if ($keynum =~ /^\d+$/) {
1308: my $privhost;
1309: my $privname = 'ltitools';
1310: if (($type eq 'lti') || ($type eq 'linkprot')) {
1311: $privname = 'lti';
1312: }
1313: if ($crsdef) {
1314: my $primary = &Apache::lonnet::domain($cdom,'primary');
1315: my @ids = &Apache::lonnet::current_machine_ids();
1316: unless (grep(/^\Q$primary\E$/,@ids)) {
1317: $privhost = $primary;
1318: my ($result,$plainsecret) = &decrypt_secret($privhost,$secret,$keynum,$privname);
1319: if ($result eq 'ok') {
1320: $secret = $plainsecret;
1321: } else {
1322: undef($secret);
1323: }
1324: }
1325: }
1326: unless ($privhost) {
1327: my $privkey = &get_dom("getdom:$cdom:private:$keynum:$privname:key");
1328: if (($privkey ne '') && ($secret ne '')) {
1329: my $cipher = new Crypt::CBC($privkey);
1330: $secret = $cipher->decrypt_hex($secret);
1331: } else {
1332: undef($secret);
1333: }
1334: }
1335: }
1336: }
1337: }
1338: }
1339: return ($key,$secret);
1340: }
1341:
1342: sub decrypt_secret {
1343: my ($privhost,$secret,$keynum,$type) = @_;
1344: return;
1345: }
1346:
1.1 droeschl 1347: 1;
1348:
1349: __END__
1350:
1351: =head1 NAME
1352:
1353: LONCAPA::Lond.pm
1354:
1355: =head1 SYNOPSIS
1356:
1357: #TODO
1358:
1359: =head1 DESCRIPTION
1360:
1361: #TODO
1362:
1363: =head1 METHODS
1364:
1365: =over 4
1366:
1.2 droeschl 1367: =item dump_with_regexp( $tail, $client )
1.1 droeschl 1368:
1369: Dump a profile database with an optional regular expression to match against
1370: the keys. In this dump, no effort is made to separate symb from version
1371: information. Presumably the databases that are dumped by this command are of a
1372: different structure. Need to look at this and improve the documentation of
1373: both this and the currentdump handler.
1374:
1375: $tail a colon separated list containing
1376:
1377: =over
1378:
1379: =item domain
1380:
1381: =item user
1382:
1383: identifying the user.
1384:
1385: =item namespace
1386:
1387: identifying the database.
1388:
1389: =item regexp
1390:
1391: optional regular expression that is matched against database keywords to do
1392: selective dumps.
1393:
1394: =item range
1395:
1396: optional range of entries e.g., 10-20 would return the 10th to 19th items, etc.
1397:
1398: =back
1399:
1400: $client is the channel open on the client.
1401:
1402: Returns: 1 (Continue processing).
1403:
1404: Side effects: response is written to $client.
1405:
1.5 bisitz 1406: =item dump_course_id_handler
1.4 droeschl 1407:
1408: #TODO copy from lond
1409:
1410: =item dump_profile_database
1411:
1412: #TODO copy from lond
1.2 droeschl 1413:
1414: =item releasereqd_check( $cnum, $cdom, $key, $value, $major, $minor,
1415: $homecourses, $ids )
1416:
1417: releasereqd_check() will determine if a LON-CAPA version (defined in the
1418: $major,$minor args passed) is not too old to allow use of a role in a
1419: course ($cnum,$cdom args passed), if at least one of the following applies:
1420: (a) the course is a Community, (b) the course's home server is *not* the
1421: current server, or (c) cached course information is not stale.
1422:
1423: For the case where none of these apply, the course is added to the
1424: $homecourse hash ref (keys = courseIDs, values = array of a hash of roles).
1425: The $homecourse hash ref is for courses for which the current server is the
1426: home server. LON-CAPA version requirements are checked elsewhere for the
1427: items in $homecourse.
1428:
1429:
1430: =item check_homecourses( $homecourses, $regexp, $count, $range, $start, $end,
1431: $major, $minor )
1432:
1433: check_homecourses() will retrieve course information for those courses which
1434: are keys of the $homecourses hash ref (first arg). The nohist_courseids.db
1435: GDBM file is tied and course information for each course retrieved. Last
1436: visit (lasttime key) is also retrieved for each, and cached values updated
1437: for any courses last visited less than 24 hours ago. Cached values are also
1438: updated for any courses included in the $homecourses hash ref.
1439:
1440: The reason for the 24 hours constraint is that the cron entry in
1441: /etc/cron.d/loncapa for /home/httpd/perl/refresh_courseids_db.pl causes
1442: cached course information to be updated nightly for courses with activity
1443: within the past 24 hours.
1444:
1445: Role information for the user (included in a ref to an array of hashes as the
1446: value for each key in $homecourses) is appended to the result returned by the
1447: routine, which will in turn be appended to the string returned to the client
1448: hosting the user's session.
1449:
1450:
1451: =item useable_role( $reqdmajor, $reqdminor, $major, $minor )
1452:
1453: useable_role() will compare the LON-CAPA version required by a course with
1454: the version available on the client server. If the client server's version
1455: is compatible, 1 will be returned.
1456:
1457:
1.3 droeschl 1458: =item get_courseinfo_hash( $cnum, $cdom, $home )
1459:
1460: get_courseinfo_hash() is used to retrieve course information from the db
1461: file: nohist_courseids.db for a course for which the current server is *not*
1462: the home server.
1463:
1464: A hash of a hash will be retrieved. The outer hash contains a single key --
1465: courseID -- for the course for which the data are being requested.
1466: The contents of the inner hash, for that single item in the outer hash
1467: are returned (and cached in memcache for 10 minutes).
1468:
1.8.2.2 raeburn 1469: =item get_dom ( $userinput )
1.3 droeschl 1470:
1.8.2.2 raeburn 1471: get_dom() will retrieve domain configuration information from a GDBM file
1472: in /home/httpd/lonUsers/$dom on the primary library server in a domain.
1473: The single argument passed is the string: $cmd:$udom:$namespace:$what
1474: where $cmd is the command historically passed to lond - i.e., getdom
1475: or egetdom, $udom is the domain, $namespace is the name of the GDBM file
1476: (encconfig or configuration), and $what is a string containing names of
1477: items to retrieve from the db file (each item name is escaped and separated
1478: from the next item name with an ampersand). The return value is either:
1479: error: followed by an error message, or a string containing the value (escaped)
1480: for each item, again separated from the next item with an ampersand.
1.3 droeschl 1481:
1.1 droeschl 1482: =back
1483:
1484: =head1 BUGS
1485:
1486: No known bugs at this time.
1487:
1488: =head1 SEE ALSO
1489:
1490: L<Apache::lonnet>, L<lond>
1491:
1492: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>