--- loncom/lonenc.pm	2004/11/16 15:26:36	1.7
+++ loncom/lonenc.pm	2011/09/30 15:58:05	1.25
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # URL translation for encrypted filenames
 #
-# $Id: lonenc.pm,v 1.7 2004/11/16 15:26:36 albertel Exp $
+# $Id: lonenc.pm,v 1.25 2011/09/30 15:58:05 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -29,52 +29,59 @@
 package Apache::lonenc;
 
 use strict;
-use Apache::Constants qw(:common :remotehost);
-use Apache::lonnet();
-use Apache::File();
-use Apache::loncommon;
+use Apache::lonnet;
 use Crypt::IDEA;
+use Time::HiRes qw(gettimeofday);
+use LONCAPA;
+ 
 
-sub handler {
-    my $r = shift;
-    my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
-    my $lonid=$cookies{'lonID'};
-    my $cookie;
-    if ($lonid) {
-	my $handle=$lonid->value;
-        $handle=~s/\W//g;
-        my $lonidsdir=$r->dir_config('lonIDsDir');
-	$ENV{'request.enc'}=1;
-        if ((-e "$lonidsdir/$handle.id") && ($handle ne '')) {
-# Initialize Environment
-            &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle);
-# Decrypt URL and redirect
-	    &Apache::lonnet::logthis("args ".$r->args);
-	    &Apache::lonnet::logthis("uri ".$r->uri);
-	    $r->internal_redirect(&unencrypted($r->uri).'?'.$r->args);
-	    return OK;
-	} 
-    }
-    return FORBIDDEN;
+#
+#  If a module makes multiple SSI calls and some of the ssi calls result in a
+#  resource for an encoded URL, and this can be done in an unprivileged role,
+#  there must be a mechanism t oreset the 'request.enc' environment variable.
+#  This sub centralizes that mechanism:
+#
+sub reset_enc {
+    $env{'request.enc'} = 0;
 }
 
 sub encryptseed {
-    my $seed=$ENV{'course.'.$ENV{'request.course.id'}.'.internal.encseed'};
-    $seed=~s/[^0-9a-f]/0/g;
-    $seed.='0123456789abcdef';
-    $seed=substr($seed.$seed,0,32);
-    return pack("H32",$seed);
+    my ($cid) = @_;
+    if (!defined($cid)) {
+        $cid = $env{'request.course.id'};
+    }
+    my $seed;
+    if (defined($cid)) {
+        if (defined$env{'course.'.$cid.'.internal.encseed'}) {
+            $seed = $env{'course.'.$cid.'.internal.encseed'};
+        } else {
+            my %descargs = ( 'one_time' => 1);
+            my %course = 
+               &Apache::lonnet::coursedescription($cid,\%descargs);
+            $seed = $course{'internal.encseed'};
+        }
+    }
+    if (defined($seed)) {
+        $seed=~s/[^0-9a-f]/0/g;
+        $seed.='0123456789abcdef';
+        $seed=substr($seed.$seed,0,32);
+        return pack("H32",$seed);
+    } else {
+        return pack("H32",1);
+    }
 }
 
 sub unencrypted {
-    my $uri=shift;
+    my ($uri,$cid) = @_;
     $uri=~s/^\/enc\/(\d+)\///;
     my $cmdlength=$1;
-    my $seed=&encryptseed();
+    # strip any added extension
+    $uri=~s/\.[^.]*//;
+    my $seed=&encryptseed($cid);
     unless ($seed) {
 	return '/'.$uri;
     }
-    $uri=&Apache::lonnet::unescape($uri);
+    $uri=&unescape($uri);
     my $cipher=new IDEA $seed;
     my $decuri='';
     for (my $encidx=0;$encidx<length($uri);$encidx+=16) {
@@ -82,31 +89,61 @@ sub unencrypted {
 				  pack("H16",substr($uri,$encidx,16))
 				  );
     }
-    $ENV{'request.enc'}=1;
+    $env{'request.enc'}=1;
+    $decuri=&remove_noise($decuri);
     return substr($decuri,0,$cmdlength);
 }
 
+# add a randomish character after every 4th caharacter
+sub add_noise {
+    my ($uri)=@_;
+    my @noise=split(/(.)/,(&gettimeofday())[1]);
+    my $noisy;
+    my $i;
+    foreach my $chunk (split(/(....)/,$uri)) {
+	$noisy.=$chunk;
+	$noisy.=$noise[($i++)%(scalar@noise)];
+    }
+    return $noisy;
+}
+
+# remove every fifth character
+sub remove_noise {
+    my ($uri)=@_;
+    my $clean;
+    foreach my $chunk (split(/(....)./,$uri)) { $clean.=$chunk; }
+    return $clean;
+}
+
 sub encrypted {
-    my $uri=shift;
-    if ($ENV{'request.role.adv'}) { return($uri); }
+    my ($uri,$force_enc) = @_;
+    if (!$force_enc && $env{'request.role.adv'}) { return($uri); }
     my $seed=&encryptseed();
     unless ($seed) {
 	return $uri;
     }
     my $cmdlength=length($uri);
-    $uri.='00000000';
+    # add noise before enc so that that same url's look different
+    $uri=&add_noise($uri);
+    my $noiselength=length($uri);
+    $uri.=time;
     my $encuri='';
     my $cipher=new IDEA $seed;
-    for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
+    for (my $encidx=0;$encidx<=$noiselength;$encidx+=8) {
 	$encuri.=unpack("H16",
 			$cipher->encrypt(substr($uri,$encidx,8)));
     }
-    return '/enc/'.$cmdlength.'/'.&Apache::lonnet::escape($encuri);
+    return '/enc/'.$cmdlength.'/'.&escape($encuri);
 }
 
 sub check_encrypt {
     my $str=shift;
-    if ($ENV{'request.enc'}) { return &Apache::lonenc::encrypted($str); }
+    if (ref($str)) {
+        if ($env{'request.enc'}) { $$str = &Apache::lonenc::encrypted($$str); }
+        return;
+    } else {
+        if ($env{'request.enc'}) { return &Apache::lonenc::encrypted($str); }
+    }
     return $str;
 }
 
@@ -120,6 +157,31 @@ sub check_decrypt {
     return $str;
 }
 
+sub encrypt_ref {
+    my ($token,$elements,$force_enc)=@_;
+    my $html;
+    if ($force_enc || $env{'request.enc'}) {
+	while (my ($name,$value)= each(%{ $elements })) {
+	    next if (!$value); 
+	    next if ($value =~ /^\w+:/); # explict javascript: or http: link
+	    my $href=&Apache::lonnet::hreflocation($Apache::lonxml::pwd[-1],$value);
+	    if ($href !~ /^https?\:/) {
+		# IE really wants an extension
+		my ($extension) = ($href =~ m/(\.[^.]*)$/);
+		my $newhref = &Apache::lonenc::encrypted($href,$force_enc);
+		unless ($newhref eq $href) {
+		    $href = $newhref.$extension;
+		}
+	    }
+	    $token->[2]->{$name}=$href;
+	}
+	delete($token->[2]->{'encrypturl'});
+	$html = &Apache::edit::rebuild_tag($token);
+    } else {
+	$html = $token->[4];
+    }
+    return $html;
+}
 1;
 __END__