--- loncom/auth/loncacc.pm	2000/12/25 12:44:43	1.12
+++ loncom/auth/loncacc.pm	2009/10/07 14:47:48	1.50
@@ -1,128 +1,196 @@
 # The LearningOnline Network
 # Cookie Based Access Handler for Construction Area
 # (lonacc: 5/21/99,5/22,5/29,5/31 Gerd Kortemeyer)
-# 6/15,16/11,22/11,
-# 01/06,01/11,6/1,9/25,9/28,11/22,12/25 Gerd Kortemeyer
+#
+# $Id: loncacc.pm,v 1.50 2009/10/07 14:47:48 raeburn Exp $
+#
+# Copyright Michigan State University Board of Trustees
+#
+# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
+#
+# LON-CAPA is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# LON-CAPA is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with LON-CAPA; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# /home/httpd/html/adm/gpl.txt
+#
+# http://www.lon-capa.org/
+#
+
+=pod
+
+=head1 NAME
+
+Apache::lonacc - Cookie Based Access Handler for Construction Area
+
+=head1 SYNOPSIS
+
+Invoked (for various locations) by /etc/httpd/conf/loncapa_apache.conf:
+
+ PerlAccessHandler       Apache::loncacc
+
+=head1 INTRODUCTION
+
+This module enables cookie based authentication for construction area
+and is used to control access for three (essentially equivalent) URIs.
+
+ <LocationMatch "^/priv.*">
+ <LocationMatch "^/\~.*">
+ <LocationMatch "^/\~.*/$">
+
+Whenever the client sends the cookie back to the server, 
+if the cookie is missing or invalid, the user is re-challenged
+for login information.
+
+This is part of the LearningOnline Network with CAPA project
+described at http://www.lon-capa.org.
+
+=head1 HANDLER SUBROUTINE
+
+This routine is called by Apache and mod_perl.
+
+=over 4
+
+=item *
+
+load POST parameters
+
+=item *
+
+store where they wanted to go (first url entered)
+
+=back
+
+=head1 OTHERSUBROUTINES
+
+=over
+
+=item constructaccess($url,$ownerdomain)
+
+See if the owner domain and name
+in the URL match those in the expected environment.  If so, return 
+two element list ($ownername,$ownerdomain).  Else, return null string.
+
+=back
+
+=cut
+
 
 package Apache::loncacc;
 
 use strict;
-use Apache::Constants qw(:common :http :methods);
-use Apache::File;
-use CGI::Cookie();
+use Apache::Constants qw(:common :http :methods REDIRECT);
+use Fcntl qw(:flock);
+use Apache::lonlocal;
+use Apache::lonnet;
+use Apache::lonacc;
+use LONCAPA qw(:DEFAULT :match);
+
+sub constructaccess {
+    my ($url,$ownerdomain,$setpriv)=@_;
+    my ($ownername)=($url=~/\/(?:\~|priv\/|home\/)($match_username)\//);
+    unless (($ownername) && ($ownerdomain)) { return ''; }
+    # We do not allow editing of previous versions of files.
+    if ($url=~/\.(\d+)\.(\w+)$/) { return ''; }
+    my @possibledomains = &Apache::lonnet::current_machine_domains();
+    if ($ownername eq $env{'user.name'}) {
+	foreach my $domain (@possibledomains) {
+	    if ($domain eq $env{'user.domain'}) {
+		return ($ownername,$domain);
+	    }
+	}
+    }
+    
+    foreach my $domain (@possibledomains) {
+	if (exists($env{'user.priv.ca./'.$domain.'/'.$ownername.'./'}) ||
+	    exists($env{'user.priv.aa./'.$domain.'/'.$ownername.'./'}) ) {
+	    return ($ownername,$domain);
+	}
+    }
+
+    my $then=$env{'user.login.time'};
+    my %dcroles = ();
+    if (&is_active_dc($ownerdomain,$then)) {
+        my %blocked=&Apache::lonnet::get('environment',['domcoord.author'],
+                                         $ownerdomain,$ownername);
+        unless ($blocked{'domcoord.author'} eq 'blocked') {
+            if (grep(/^$ownerdomain$/,@possibledomains)) {
+                if ($setpriv) {
+                    my $now = time;
+                    &Apache::lonnet::check_adhoc_privs($ownerdomain,$ownername,
+                                                       $then,$now,'ca');
+                }
+                return($ownername,$ownerdomain);
+            }
+        }
+    }
+    return '';
+}
+
+sub is_active_dc {
+    my ($ownerdomain,$then) = @_;
+    my $livedc;
+    if ($env{'user.adv'}) {
+        my $domrole = $env{'user.role.dc./'.$ownerdomain.'/'};
+        if ($domrole) {
+            my ($tstart,$tend)=split(/\./,$domrole);
+            $livedc = 1;
+            if ($tstart && $tstart>$then) { undef($livedc); }
+            if ($tend   && $tend  <$then) { undef($livedc); }
+        }
+    }
+    return $livedc;
+}
+
 
 sub handler {
     my $r = shift;
     my $requrl=$r->uri;
-    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');
-        if ((-e "$lonidsdir/$handle.id") && ($handle ne '')) {
-            my $ownername=$requrl;
-            $ownername=~s/\/(?:\~|priv\/)(\w+).*/\1/;
-            my $ownerdomain=$r->dir_config('lonDefDomain');
-            my @handleparts=split(/\_/,$handle);
-            my $username=$handleparts[0];
-            my $domain=$handleparts[2];
-            if (($username ne $ownername) || ($domain ne $ownerdomain)) {
-                $r->log_reason
-                   ("$username at $domain not authorized", $r->filename); 
-	        return HTTP_NOT_ACCEPTABLE;
-            }
-            my @profile;
-	    {
-             my $idf=Apache::File->new("$lonidsdir/$handle.id");
-             @profile=<$idf>;
-	    }
-            my $envi;
-            for ($envi=0;$envi<=$#profile;$envi++) {
-		chomp($profile[$envi]);
-		my ($envname,$envvalue)=split(/=/,$profile[$envi]);
-                $ENV{$envname} = $envvalue;
-            }
-            $ENV{'user.environment'} = "$lonidsdir/$handle.id";
-            $ENV{'request.state'}    = "construct";
-            $ENV{'request.filename'} = $r->filename;
+    $env{'request.editurl'}=$requrl;
 
-# -------------------------------------------------------- Load POST parameters
+    my $handle =  &Apache::lonnet::check_for_valid_session($r);
+    if ($handle ne '') {
 
+# ------------------------------------------------------ Initialize Environment
+        my $lonidsdir=$r->dir_config('lonIDsDir');
+	&Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle);
 
-        my $buffer;
+# --------------------------------------------------------- Initialize Language
+ 
+	&Apache::lonlocal::get_language_handle($r);
 
-        $r->read($buffer,$r->header_in('Content-length'));
+# -------------------------------------------------------------- Resource State
 
-	unless ($buffer=~/^(\-+\w+)\s+Content\-Disposition\:\s*form\-data/si) {
-            my @pairs=split(/&/,$buffer);
-            my $pair;
-            foreach $pair (@pairs) {
-               my ($name,$value) = split(/=/,$pair);
-               $value =~ tr/+/ /;
-               $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
-               $name  =~ tr/+/ /;
-               $name  =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
-               $ENV{"form.$name"}=$value;
-            } 
-        } else {
-	    my $contentsep=$1;
-            my @lines = split (/\n/,$buffer);
-            my $name='';
-            my $value='';
-            my $fname='';
-            my $fmime='';
-            my $i;
-            for ($i=0;$i<=$#lines;$i++) {
-		if ($lines[$i]=~/^$contentsep/) {
-		    if ($name) {
-                        chomp($value);
-                        $ENV{"form.$name"}=$value;
-			if ($fname) {
-			    $ENV{"form.$name.filename"}=$fname;
-                            $ENV{"form.$name.mimetype"}=$fmime;
-                        }
-                    }
-                    if ($i<$#lines) {
-			$i++;
-                        $lines[$i]=~
-		 /Content\-Disposition\:\s*form\-data\;\s*name\=\"([^\"]+)\"/i;
-                        $name=$1;
-                        $value='';
-                        if ($lines[$i]=~/filename\=\"([^\"]+)\"/i) {
-			   $fname=$1;
-                           if 
-                            ($lines[$i+1]=~/Content\-Type\:\s*([\w\-\/]+)/i) {
-			      $fmime=$1;
-                              $i++;
-			   } else {
-                              $fmime='';
-                           }
-                        } else {
-			    $fname='';
-                            $fmime='';
-                        }
-                        $i++;
-                    }
-                } else {
-		    $value.=$lines[$i]."\n";
-                }
-            }
+	$env{'request.state'}    = "construct";
+	$env{'request.filename'} = $r->filename;
+
+	unless (&constructaccess($requrl,$r->dir_config('lonDefDomain'),'setpriv')) {
+	    $r->log_reason("Unauthorized $requrl", $r->filename); 
+	    return HTTP_NOT_ACCEPTABLE;
 	}
-            $r->method_number(M_GET);
-	    $r->method('GET');
-            $r->headers_in->unset('Content-length');
-
-            return OK; 
-        } else { 
-            $r->log_reason("Cookie $handle not valid", $r->filename) 
-        };
+
+# -------------------------------------------------------- Load POST parameters
+
+	&Apache::lonacc::get_posted_cgi($r);
+
+	return OK; 
+    } else { 
+	$r->log_reason("Cookie $handle not valid", $r->filename) 
     }
 
 # ----------------------------------------------- Store where they wanted to go
 
-    $ENV{'request.firsturl'}=$requrl;
+    $env{'request.firsturl'}=$requrl;
     return FORBIDDEN;
 }