--- loncom/lonnet/perl/lonnet.pm	2007/04/04 00:10:15	1.863
+++ loncom/lonnet/perl/lonnet.pm	2007/06/07 22:09:59	1.885
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.863 2007/04/04 00:10:15 albertel Exp $
+# $Id: lonnet.pm,v 1.885 2007/06/07 22:09:59 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -31,22 +31,21 @@ package Apache::lonnet;
 
 use strict;
 use LWP::UserAgent();
-use HTTP::Headers;
 use HTTP::Date;
 # use Date::Parse;
-use vars 
-qw(%perlvar %badServerCache %spareid 
-   %pr %prp $memcache %packagetab 
-   %courselogs %accesshash %userrolehash %domainrolehash $processmarker $dumpcount 
-   %coursedombuf %coursenumbuf %coursehombuf %coursedescrbuf %courseinstcodebuf %courseownerbuf %coursetypebuf
-   $tmpdir $_64bit %env);
+use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir
+            $_64bit %env);
+
+my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash,
+    %userrolehash, $processmarker, $dumpcount, %coursedombuf,
+    %coursenumbuf, %coursehombuf, %coursedescrbuf, %courseinstcodebuf,
+    %courseownerbuf, %coursetypebuf);
 
 use IO::Socket;
 use GDBM_File;
 use HTML::LCParser;
-use HTML::Parser;
 use Fcntl qw(:flock);
-use Storable qw(lock_store lock_nstore lock_retrieve freeze thaw nfreeze);
+use Storable qw(thaw nfreeze);
 use Time::HiRes qw( gettimeofday tv_interval );
 use Cache::Memcached;
 use Digest::MD5;
@@ -182,7 +181,7 @@ sub subreply {
 	$client=IO::Socket::UNIX->new(Peer    =>"$peerfile",
 				      Type    => SOCK_STREAM,
 				      Timeout => 10);
-	if($client) {
+	if ($client) {
 	    last;		# Connected!
 	} else {
 	    &create_connection(&hostname($server),$server);
@@ -745,7 +744,7 @@ sub get_dom {
         if (defined(&domain($udom,'primary'))) {
             $uhome=&domain($udom,'primary');
         } else {
-            $uhome eq '';
+            undef($uhome);
         }
     } else {
         if (!$uhome) {
@@ -756,11 +755,14 @@ sub get_dom {
     }
     if ($udom && $uhome && ($uhome ne 'no_host')) {
         my $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
+        my %returnhash;
+        if ($rep eq '' || $rep =~ /^error: 2 /) {
+            return %returnhash;
+        }
         my @pairs=split(/\&/,$rep);
         if ( $#pairs==0 && $pairs[0] =~ /^(con_lost|error|no_such_host)/i) {
             return @pairs;
         }
-        my %returnhash=();
         my $i=0;
         foreach my $item (@$storearr) {
             $returnhash{$item}=&thaw_unescape($pairs[$i]);
@@ -768,7 +770,7 @@ sub get_dom {
         }
         return %returnhash;
     } else {
-        &logthis("get_dom failed - no homeserver and/or domain");
+        &logthis("get_dom failed - no homeserver and/or domain ($udom) ($uhome)");
     }
 }
 
@@ -781,7 +783,7 @@ sub put_dom {
         if (defined(&domain($udom,'primary'))) {
             $uhome=&domain($udom,'primary');
         } else {
-            $uhome eq '';
+            undef($uhome);
         }
     } else {
         if (!$uhome) {
@@ -826,6 +828,16 @@ sub retrieve_inst_usertypes {
     return (\%returnhash,\@order);
 }
 
+sub is_domainimage {
+    my ($url) = @_;
+    if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo)/+-) {
+        if (&domain($1) ne '') {
+            return '1';
+        }
+    }
+    return;
+}
+
 # --------------------------------------------------- Assign a key to a student
 
 sub assign_access_key {
@@ -1052,7 +1064,10 @@ my $kicks=0;
 my $hits=0;
 sub make_key {
     my ($name,$id) = @_;
-    if (length($id) > 200) { $id=length($id).':'.&Digest::MD5::md5_hex($id); }
+    if (length($id) > 65 
+	&& length(&escape($id)) > 200) {
+	$id=length($id).':'.&Digest::MD5::md5_hex($id);
+    }
     return &escape($name.':'.$id);
 }
 
@@ -1099,7 +1114,9 @@ sub do_cache_new {
 	$time=600;
     }
     if ($debug) { &Apache::lonnet::logthis("Setting $id to $value"); }
-    $memcache->set($id,$setvalue,$time);
+    if (!($memcache->set($id,$setvalue,$time))) {
+	&logthis("caching of id -> $id  failed");
+    }
     # need to make a copy of $value
     #&make_room($id,$value,$debug);
     return $value;
@@ -2133,7 +2150,12 @@ sub get_my_roles {
     my %returnhash=();
     my $now=time;
     foreach my $entry (keys(%dumphash)) {
-	my ($tend,$tstart)=split(/\:/,$dumphash{$entry});
+        my ($role,$tend,$tstart);
+        if ($context eq 'userroles') {
+	    ($role,$tend,$tstart)=split(/_/,$dumphash{$entry});
+        } else {
+            ($tend,$tstart)=split(/\:/,$dumphash{$entry});
+        }
         if (($tstart) && ($tstart<0)) { next; }
         my $status = 'active';
         if (($tend) && ($tend<$now)) {
@@ -2151,7 +2173,13 @@ sub get_my_roles {
                 next;
             }
         }
-        my ($role,$username,$domain,$section)=split(/\:/,$entry);
+        my ($rolecode,$username,$domain,$section,$area);
+        if ($context eq 'userroles') {
+            ($area,$rolecode) = split(/_/,$entry);
+            (undef,$domain,$username,$section) = split(/\//,$area);
+        } else {
+            ($role,$username,$domain,$section) = split(/\:/,$entry);
+        }
         if (ref($roledoms) eq 'ARRAY') {
             if (!grep(/^\Q$domain\E$/,@{$roledoms})) {
                 next;
@@ -2161,7 +2189,7 @@ sub get_my_roles {
             if (!grep(/^\Q$role\E$/,@{$roles})) {
                 next;
             }
-        } 
+        }
 	$returnhash{$username.':'.$domain.':'.$role}=$tstart.':'.$tend;
     }
     return %returnhash;
@@ -3105,7 +3133,7 @@ sub set_userprivs {
     if (keys(%{$allgroups}) > 0) {
         foreach my $role (keys %{$allroles}) {
             my ($trole,$area,$sec,$extendedarea);
-            if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)-) {
+            if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)\.-) {
                 $trole = $1;
                 $area = $2;
                 $sec = $3;
@@ -3586,9 +3614,16 @@ sub get_portfolio_access {
             }
             if (@users > 0) {
                 foreach my $userkey (@users) {
-                    if (exists($access_hash->{$userkey}{'users'}{$env{'user.name'}.':'.$env{'user.domain'}})) {
-                        return 'ok';
-                    }
+                    if (ref($access_hash->{$userkey}{'users'}) eq 'ARRAY') {
+                        foreach my $item (@{$access_hash->{$userkey}{'users'}}) {
+                            if (ref($item) eq 'HASH') {
+                                if (($item->{'uname'} eq $env{'user.name'}) &&
+                                    ($item->{'udom'} eq $env{'user.domain'})) {
+                                    return 'ok';
+                                }
+                            }
+                        }
+                    } 
                 }
             }
             my %roleshash;
@@ -4419,8 +4454,18 @@ sub userlog_query {
 
 sub auto_run {
     my ($cnum,$cdom) = @_;
-    my $homeserver = &homeserver($cnum,$cdom);
-    my $response = &reply('autorun:'.$cdom,$homeserver);
+    my $response = 0;
+    my $settings;
+    my %domconfig = &get_dom('configuration',['autoenroll'],$cdom);
+    if (ref($domconfig{'autoenroll'}) eq 'HASH') {
+        $settings = $domconfig{'autoenroll'};
+        if ($settings->{'run'} eq '1') {
+            $response = 1;
+        }
+    } else {
+        my $homeserver = &homeserver($cnum,$cdom);
+        $response = &reply('autorun:'.$cdom,$homeserver);
+    }
     return $response;
 }
 
@@ -4450,15 +4495,27 @@ sub auto_validate_courseID {
 }
 
 sub auto_create_password {
-    my ($cnum,$cdom,$authparam) = @_;
-    my $homeserver = &homeserver($cnum,$cdom); 
+    my ($cnum,$cdom,$authparam,$udom) = @_;
+    my ($homeserver,$response);
     my $create_passwd = 0;
     my $authchk = '';
-    my $response=&unescape(&reply('autocreatepassword:'.$authparam.':'.$cdom,$homeserver));
-    if ($response eq 'refused') {
-        $authchk = 'refused';
+    if ($udom =~ /^$match_domain$/) {
+        $homeserver = &domain($udom,'primary');
+    }
+    if ($homeserver eq '') {
+        if (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)) {
+            $homeserver = &homeserver($cnum,$cdom);
+        }
+    }
+    if ($homeserver eq '') {
+        $authchk = 'nodomain';
     } else {
-        ($authparam,$create_passwd,$authchk) = split/:/,$response;
+        $response=&unescape(&reply('autocreatepassword:'.$authparam.':'.$cdom,$homeserver));
+        if ($response eq 'refused') {
+            $authchk = 'refused';
+        } else {
+            ($authparam,$create_passwd,$authchk) = split/:/,$response;
+        }
     }
     return ($authparam,$create_passwd,$authchk);
 }
@@ -5263,7 +5320,7 @@ sub save_selected_files {
     my ($user, $path, @files) = @_;
     my $filename = $user."savedfiles";
     my @other_files = &files_not_in_path($user, $path);
-    open (OUT, '>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename);
+    open (OUT, '>'.$tmpdir.$filename);
     foreach my $file (@files) {
         print (OUT $env{'form.currentpath'}.$file."\n");
     }
@@ -5855,6 +5912,13 @@ sub devalidatecourseresdata {
 
 
 # --------------------------------------------------- Course Resourcedata Query
+#
+#  Parameters:
+#      $coursenum    - Number of the course.
+#      $coursedomain - Domain at which the course was created.
+#  Returns:
+#     A hash of the course parameters along (I think) with timestamps
+#     and version info.
 
 sub get_courseresdata {
     my ($coursenum,$coursedomain)=@_;
@@ -5913,7 +5977,21 @@ sub get_userresdata {
     }
     return $tmp;
 }
-
+#----------------------------------------------- resdata - return resource data
+#  Purpose:
+#    Return resource data for either users or for a course.
+#  Parameters:
+#     $name      - Course/user name.
+#     $domain    - Name of the domain the user/course is registered on.
+#     $type      - Type of thing $name is (must be 'course' or 'user'
+#     @which     - Array of names of resources desired.
+#  Returns:
+#     The value of the first reasource in @which that is found in the
+#     resource hash.
+#  Exceptional Conditions:
+#     If the $type passed in is not valid (not the string 'course' or 
+#     'user', an undefined  reference is returned.
+#     If none of the resources are found, an undef is returned
 sub resdata {
     my ($name,$domain,$type,@which)=@_;
     my $result;
@@ -6260,7 +6338,7 @@ sub packages_tab_default {
 	    $do_default=1;
 	} elsif ($pack_type eq 'extension') {
 	    push(@extension,[$package,$pack_type,$pack_part]);
-	} elsif ($pack_part eq $part) {
+	} elsif ($pack_part eq $part || $pack_type eq 'part') {
 	    # only look at packages defaults for packages that this id is
 	    push(@specifics,[$package,$pack_type,$pack_part]);
 	}
@@ -6477,13 +6555,18 @@ sub metadata {
 	    }
 	}
 	my ($extension) = ($uri =~ /\.(\w+)$/);
+	$extension = lc($extension);
+	if ($extension eq 'htm') { $extension='html'; }
+
 	foreach my $key (keys(%packagetab)) {
 	    #no specific packages #how's our extension
 	    if ($key!~/^extension_\Q$extension\E&/) { next; }
 	    &metadata_create_package_def($uri,$key,'extension_'.$extension,
 					 \%metathesekeys);
 	}
-	if (!exists($metaentry{':packages'})) {
+
+	if (!exists($metaentry{':packages'})
+	    || $packagetab{"import_defaults&extension_$extension"}) {
 	    foreach my $key (keys(%packagetab)) {
 		#no specific packages well let's get default then
 		if ($key!~/^default&/) { next; }
@@ -7432,6 +7515,7 @@ sub filelocation {
 	$file=~s-^/adm/wrapper/-/-;
 	$file=~s-^/adm/coursedocs/showdoc/-/-;
     }
+
     if ($file=~m:^/~:) { # is a contruction space reference
         $location = $file;
         $location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;
@@ -7452,6 +7536,8 @@ sub filelocation {
   	  $location=$Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles/'.
   	      $udom.'/'.$uname.'/'.$filename;
         }
+    } elsif ($file =~ m-^/adm/-) {
+	$location = $perlvar{'lonDocRoot'}.'/'.$file;
     } else {
         $file=~s/^\Q$perlvar{'lonDocRoot'}\E//;
         $file=~s:^/res/:/:;
@@ -7562,7 +7648,8 @@ sub declutter {
 
 sub clutter {
     my $thisfn='/'.&declutter(shift);
-    unless ($thisfn=~/^\/(uploaded|editupload|adm|userfiles|ext|raw|priv|public)\//) { 
+    if ($thisfn !~ m{^/(uploaded|editupload|userfiles|ext|raw|priv|public)/}
+	|| $thisfn =~ m{^/adm/(includes|pages)} ) { 
        $thisfn='/res'.$thisfn; 
     }
     if ($thisfn !~m|/adm|) {
@@ -7631,18 +7718,18 @@ sub correct_line_ends {
 sub goodbye {
    &logthis("Starting Shut down");
 #not converted to using infrastruture and probably shouldn't be
-   &logthis(sprintf("%-20s is %s",'%badServerCache',length(&freeze(\%badServerCache))));
+   &logthis(sprintf("%-20s is %s",'%badServerCache',length(&nfreeze(\%badServerCache))));
 #converted
 #   &logthis(sprintf("%-20s is %s",'%metacache',scalar(%metacache)));
-   &logthis(sprintf("%-20s is %s",'%homecache',length(&freeze(\%homecache))));
-#   &logthis(sprintf("%-20s is %s",'%titlecache',length(&freeze(\%titlecache))));
-#   &logthis(sprintf("%-20s is %s",'%courseresdatacache',length(&freeze(\%courseresdatacache))));
+   &logthis(sprintf("%-20s is %s",'%homecache',length(&nfreeze(\%homecache))));
+#   &logthis(sprintf("%-20s is %s",'%titlecache',length(&nfreeze(\%titlecache))));
+#   &logthis(sprintf("%-20s is %s",'%courseresdatacache',length(&nfreeze(\%courseresdatacache))));
 #1.1 only
-#   &logthis(sprintf("%-20s is %s",'%userresdatacache',length(&freeze(\%userresdatacache))));
-#   &logthis(sprintf("%-20s is %s",'%getsectioncache',length(&freeze(\%getsectioncache))));
-#   &logthis(sprintf("%-20s is %s",'%courseresversioncache',length(&freeze(\%courseresversioncache))));
-#   &logthis(sprintf("%-20s is %s",'%resversioncache',length(&freeze(\%resversioncache))));
-   &logthis(sprintf("%-20s is %s",'%remembered',length(&freeze(\%remembered))));
+#   &logthis(sprintf("%-20s is %s",'%userresdatacache',length(&nfreeze(\%userresdatacache))));
+#   &logthis(sprintf("%-20s is %s",'%getsectioncache',length(&nfreeze(\%getsectioncache))));
+#   &logthis(sprintf("%-20s is %s",'%courseresversioncache',length(&nfreeze(\%courseresversioncache))));
+#   &logthis(sprintf("%-20s is %s",'%resversioncache',length(&nfreeze(\%resversioncache))));
+   &logthis(sprintf("%-20s is %s",'%remembered',length(&nfreeze(\%remembered))));
    &logthis(sprintf("%-20s is %s",'kicks',$kicks));
    &logthis(sprintf("%-20s is %s",'hits',$hits));
    &flushcourselogs();
@@ -7650,19 +7737,41 @@ sub goodbye {
 }
 
 sub get_dns {
-    my ($url,$func) = @_;
+    my ($url,$func,$ignore_cache) = @_;
+    if (!$ignore_cache) {
+	my ($content,$cached)=
+	    &Apache::lonnet::is_cached_new('dns',$url);
+	if ($cached) {
+	    &$func($content);
+	    return;
+	}
+    }
+
+    my %alldns;
     open(my $config,"<$perlvar{'lonTabDir'}/hosts.tab");
     foreach my $dns (<$config>) {
 	next if ($dns !~ /^\^(\S*)/x);
-	$dns = $1;
+	$alldns{$1} = 1;
+    }
+    while (%alldns) {
+	my ($dns) = keys(%alldns);
+	delete($alldns{$dns});
 	my $ua=new LWP::UserAgent;
 	my $request=new HTTP::Request('GET',"http://$dns$url");
 	my $response=$ua->request($request);
 	next if ($response->is_error());
 	my @content = split("\n",$response->content);
+	&Apache::lonnet::do_cache_new('dns',$url,\@content,30*24*60*60);
 	&$func(\@content);
+	return;
     }
     close($config);
+    my $which = (split('/',$url))[3];
+    &logthis("unable to contact DNS defaulting to on disk file dns_$which.tab\n");
+    open($config,"<$perlvar{'lonTabDir'}/dns_$which.tab");
+    my @content = <$config>;
+    &$func(\@content);
+    return;
 }
 # ------------------------------------------------------------ Read domain file
 {
@@ -7685,9 +7794,15 @@ sub get_dns {
 	    $domain{$name} = \%this_domain;
 	}
     }
-    
+
+    sub reset_domain_info {
+	undef($loaded);
+	undef(%domain);
+    }
+
     sub load_domain_tab {
-	&get_dns('/adm/dns/domain',\&parse_domain_tab);
+	my ($ignore_cache) = @_;
+	&get_dns('/adm/dns/domain',\&parse_domain_tab,$ignore_cache);
 	my $fh;
 	if (open($fh,"<".$perlvar{'lonTabDir'}.'/domain.tab')) {
 	    my @lines = <$fh>;
@@ -7733,9 +7848,19 @@ sub get_dns {
 	    }
 	}
     }
+    
+    sub reset_hosts_info {
+	&reset_domain_info();
+	&reset_hosts_ip_info();
+	undef(%hostname);
+	undef(%hostdom);
+	undef(%libserv);
+	undef($loaded);
+    }
 
     sub load_hosts_tab {
-	&get_dns('/adm/dns/hosts',\&parse_hosts_tab);
+	my ($ignore_cache) = @_;
+	&get_dns('/adm/dns/hosts',\&parse_hosts_tab,$ignore_cache);
 	open(my $config,"<$perlvar{'lonTabDir'}/hosts.tab");
 	my @config = <$config>;
 	&parse_hosts_tab(\@config);
@@ -7811,6 +7936,25 @@ sub get_dns {
     my %iphost;
     my %name_to_ip;
     my %lonid_to_ip;
+
+    my %valid_ip;
+    sub valid_ip {
+	my ($ip) = @_;
+	if (exists($iphost{$ip}) || exists($valid_ip{$ip})) {
+	    return 1;	
+	}
+	my $name = gethostbyip($ip);
+	my $lonid = &hostname($name);
+	if (defined($lonid)) {
+	    $valid_ip{$ip} = $lonid;
+	    return 1;
+	}
+	my %iphosts = &get_iphost();
+	if (ref($iphost{$ip})) {
+	    return 1;	
+	}
+    }
+
     sub get_hosts_from_ip {
 	my ($ip) = @_;
 	my %iphosts = &get_iphost();
@@ -7819,6 +7963,12 @@ sub get_dns {
 	}
 	return;
     }
+    
+    sub reset_hosts_ip_info {
+	undef(%iphost);
+	undef(%name_to_ip);
+	undef(%lonid_to_ip);
+    }
 
     sub get_host_ip {
 	my ($lonid) = @_;
@@ -7835,10 +7985,23 @@ sub get_dns {
     }
     
     sub get_iphost {
-	if (%iphost) { return %iphost; }
+	my ($ignore_cache) = @_;
+	if (!$ignore_cache) {
+	    if (%iphost) {
+		return %iphost;
+	    }
+	    my ($ip_info,$cached)=
+		&Apache::lonnet::is_cached_new('iphost','iphost');
+	    if ($cached) {
+		%iphost      = %{$ip_info->[0]};
+		%name_to_ip  = %{$ip_info->[1]};
+		%lonid_to_ip = %{$ip_info->[2]};
+		return %iphost;
+	    }
+	}
 	my %hostname = &all_hostnames();
 	foreach my $id (keys(%hostname)) {
-	    my $name=$hostname{$id};
+	    my $name=&hostname($id);
 	    my $ip;
 	    if (!exists($name_to_ip{$name})) {
 		$ip = gethostbyname($name);
@@ -7854,6 +8017,10 @@ sub get_dns {
 	    $lonid_to_ip{$id} = $ip;
 	    push(@{$iphost{$ip}},$id);
 	}
+	&Apache::lonnet::do_cache_new('iphost','iphost',
+				      [\%iphost,\%name_to_ip,\%lonid_to_ip],
+				      24*60*60);
+
 	return %iphost;
     }
 }
@@ -8393,6 +8560,14 @@ setting for a specific $type, where $typ
 @what should be a list of parameters to ask about. This routine caches
 answers for 5 minutes.
 
+=item *
+
+get_courseresdata($courseid, $domain) : dump the entire course resource
+data base, returning a hash that is keyed by the resource name and has
+values that are the resource value.  I believe that the timestamps and
+versions are also returned.
+
+
 =back
 
 =head2 Course Modification
@@ -9075,3 +9250,4 @@ symblist($mapname,%newhash) : update sym
 =back
 
 =cut
+