#!/usr/bin/perl
$|=1;
# Script to complete processing of self-enrollment requests
# queued pending validation, when validated.
#  
# $Id: enrollqueued.pl,v 1.2 2014/04/05 22:42:33 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

enrollqueued.pl

=head1 SYNOPSIS

CGI script to process queued self-enrollment request 
and output URL which user will return to if enrollment
successful.   

Data expected by enrollqueued.pl are the same fields
as included for a POST to the external validation site,
as specified in the domain configuration for
self-enrollment validation, which can be some or all of: 

1. Unique six-character code
2. courseID (domain_coursenum)
3. student's username
4. student's domain
5. token

Either 1 or 2 are required, and 3 is required. If 4 is
not provided, the student's domain will be assumed to
be the same as the course (from 2).

The data can be passed either in a query string or as
POSTed form variables.

=head1 Subroutines

=over 4

=cut

#############################################
#############################################

use strict;

use lib '/home/httpd/lib/perl/';
use LONCAPA::loncgi;
use Apache::lonnet();
use Apache::loncommon();
use Apache::lonuserutils();
use Apache::loncoursequeueadmin();
use Apache::lonlocal;
use LONCAPA;
use IO::Socket;

&main();
exit 0;

#############################################
#############################################

=pod

=item main()

Inputs: None

Returns: Nothing

Description: Main program. Determines if requesting IP is the IP 
             of the server enrollqueued.pl. Side effects
             prints content (with text/plain HTTP header).
             Content is URL self-enrolling user should user
             to access the course. 

=cut

#############################################
#############################################

sub main {
    my $query = CGI->new();

    my @okdoms = &Apache::lonnet::current_machine_domains();

    my $perlvar = &LONCAPA::Configuration::read_conf();
    my $lonidsdir;
    if (ref($perlvar) eq 'HASH') {
        $lonidsdir = $perlvar->{'lonIDsDir'};
    }
    undef($perlvar);

    my $dom;
    if ($query->param('course')) {
        my $course = $query->param('course'); 
        $course =~ s/^\s+|\s+$//g;
        if ($course =~ /^($LONCAPA::match_domain)_($LONCAPA::match_courseid)$/) {
            my $possdom = $1;
            my $domdesc = &Apache::lonnet::domain($possdom);
            unless ($domdesc eq '') {
                $dom = $possdom;
            }
        }
    }

    if ($dom eq '') {
        if ($query->param('domain')) {
            my $possdom = $query->param('domain');
            $possdom =~ s/^\s+|\s+$//g;
            if ($possdom =~ /^$LONCAPA::match_domain$/) {
                my $domdesc = &Apache::lonnet::domain($possdom);
                unless ($domdesc eq '') {
                    $dom = $possdom;
                }
            }
        }
    }

    if ($dom eq '') {
        $dom = &Apache::lonnet::default_login_domain();
    }

    if ($dom eq '') {
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
        return;
    }

    if (!grep(/^\Q$dom\E$/,@okdoms)) {
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
        return;
    }

    my %domconfig = &Apache::lonnet::get_dom('configuration',['selfenrollment'],$dom);
    my $remote_ip = $ENV{'REMOTE_ADDR'};
    my $allowed;

    if (ref($domconfig{'selfenrollment'}) eq 'HASH') {
        if (ref($domconfig{'selfenrollment'}{'validation'}) eq 'HASH') {
            if ($domconfig{'selfenrollment'}{'validation'}{'url'} =~ m{^https?://([^/]+)/}) {
                my $ip = gethostbyname($1);
                if ($ip ne '') {
                    my $validator_ip = inet_ntoa($ip);
                    if (($validator_ip ne '') && ($remote_ip eq $validator_ip)) {
                        $allowed = 1;
                    }
                }
            } elsif ($domconfig{'selfenrollment'}{'validation'}{'url'} =~ m{^/}) {
                if ($remote_ip ne '') {
                    if (($remote_ip eq '127.0.0.1') || ($remote_ip eq $ENV{'SERVER_ADDR'})) {
                        $allowed = 1;
                    }
                }
            }
        }
    }

    my (%params,@fields,$numrequired);
    if ($allowed) {
        &Apache::lonlocal::get_language_handle();
        my ($validreq,@fields);
        if (ref($domconfig{'selfenrollment'}) eq 'HASH') {
            if (ref($domconfig{'selfenrollment'}{'validation'}) eq 'HASH') {
                if (ref($domconfig{'selfenrollment'}{'validation'}{'fields'}) eq 'ARRAY') {
                    $numrequired = scalar(@{$domconfig{'selfenrollment'}{'validation'}{'fields'}});
                    foreach my $field (@{$domconfig{'selfenrollment'}{'validation'}{'fields'}}) {
                        $params{$field} = $query->param($field);
                        if ($field eq 'username') {
                            if ($query->param($field) =~ /^LONCAPA::match_username$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'domain') {
                            if ($query->param($field) =~ /^LONCAPA::match_username$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'course') {
                            if ($query->param($field) =~ /^(?:LONCAPA::match_domain)_(?:LONCAPA::match_courseid)$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'coursetype') {
                            if ($query->param($field) =~ /^(official|unofficial|community|textbook)$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'uniquecode') {
                            if ($query->param($field) =~ /^\w{6}$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'description') {
                            $params{$field} = $query->param($field);
                        }
                    }
                    if ($numrequired == scalar(keys(%params))) {
                        $validreq = 1;
                    }
                }
            }
        }
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
        if ($validreq) {
            print(&process_enrollment($dom,$lonidsdir,\%params,\@fields));
        }
    } else {
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
    }
    return;
}

#############################################
#############################################

=pod

=item process_enrollment()

Inputs: $dom - domain of course for which enrollment is to be processed
        $lonidsdir - Path to directory containing session files for users.
                     Perl var lonIDsDir is read from loncapa_apache.conf
                     in &main() and passed as third arg to process_enrollment().
        $params - references to hash of key=value pairs from input
                  (either query string or POSTed). Keys which will be
                  used are fields specified in domain configuration
                  for self-enrollment validation.

Returns: $output - output to display.
                   If processing of the pending self-enrollment succeeds,
                   a URL is returned which may be used by the user to access 
                   the course.

Description: Processes a pending self-enrollment request, given the username
             domain, and courseID or six character code for the course.

=cut

#############################################
#############################################

sub process_enrollment {
    my ($dom,$lonidsdir,$params) = @_;
    return unless (ref($params) eq 'HASH');

    my $cid = $params->{'course'};
    my $uname = $params->{'username'};
    my $udom = $params->{'domain'};
    my $token = $params->{'token'};
    my $uhome = &Apache::lonnet::homeserver($uname,$udom);
    return if ($uhome eq 'nohost');
    my %courseinfo;
    if ($cid eq '') {
        if ($params->{'uniquecode'}) {
            my $uniquecode = $params->{'uniquecode'}; 
            my $confname = $dom.'-domainconfig';
            my %codes = &Apache::lonnet::get('uniquecodes',[$uniquecode],$dom,$confname);
            if ($codes{$uniquecode}) {
                $cid = $dom.'_'.$codes{$uniquecode};
            }
        }
    }
    return if ($cid eq '');
    my $url;
    if ($cid) {
        %courseinfo = &Apache::lonnet::coursedescription($cid,{one_time => 1});
        if ($courseinfo{'description'} ne '') {
            my $cdom = $courseinfo{'domain'};
            my $cnum = $courseinfo{'num'};
            my %requesthash = &Apache::lonnet::get('selfenrollrequests',[$cid],$udom,$uname);
            if (ref($requesthash{$cid}) eq 'HASH') {
                if ($requesthash{$cid}{status} eq 'pending') {
                    my ($lonhost,$hostname,$handle);
                    $lonhost = $requesthash{$cid}{'lonhost'};
                    if ($lonhost ne '') {
                        $hostname = &Apache::lonnet::hostname($lonhost);
                    }
                    my $savedtoken = $requesthash{$cid}{'token'};
                    my $enroll = 1;
                    if ($token ne '') {
                        if ($token ne $savedtoken) {
                            $enroll = 0;
                        }
                    }
                    if ($enroll) {
                        my $handle = $requesthash{$cid}{'handle'}; 
                        my $usec = $courseinfo{'internal.selfenroll_section'};
                        my $access_start = $courseinfo{'internal.selfenroll_start_access'};
                        my $access_end = $courseinfo{'internal.selfenroll_end_access'};
                        my $limit = $courseinfo{'internal.selfenroll_limit'};
                        my $cap = $courseinfo{'internal.selfenroll_cap'};
                        my $notifylist = $courseinfo{'internal.selfenroll_notifylist'};
                        my ($stucounts,$idx,$classlist) = &get_student_counts($cdom,$cnum);
                        if (($limit eq 'allstudents') || ($limit eq 'selfenrolled')) {
                            if ($stucounts->{$limit} >= $cap) {
                                return;
                            }
                        }
                        $Apache::lonnet::env{'user.name'} = $uname;
                        $Apache::lonnet::env{'user.domain'} = $udom;
                        my $result =
                            &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
                                undef,undef,undef,$usec,$access_end,$access_start,'selfenroll',
                                undef,$cid,1);
                        delete($Apache::lonnet::env{'user.name'});
                        delete($Apache::lonnet::env{'user.domain'});
                        if ($result eq 'ok') {
                            my %userrequest = (
                                $cdom.'_'.$cnum => {
                                                     timestamp   => time,
                                                     section     => $usec,
                                                     adjudicator => 'enrollqueued',
                                                     status      => 'approved',
                                                   },
                            );
                            my $userresult =
                                &Apache::lonnet::put('selfenrollrequests',\%userrequest,$udom,$uname);
#
# check for session for this user
# if session, construct URL point at check for new roles.
#
                            my @hosts = &Apache::lonnet::current_machine_ids();
                            if (grep(/^\Q$lonhost\E$/,@hosts) && ($handle) && ($hostname)) {
                                if ($lonidsdir ne '') {
                                    if (-e "$lonidsdir/$handle.id") {
                                        my $protocol = $Apache::lonnet::protocol{$lonhost};
                                        $protocol = 'http' if ($protocol ne 'https');
                                        $url = $protocol.'://'.$hostname.'/adm/roles?state=doupdate';
                                    }
                                }
                            }
         
#
# otherwise point at default portal, or if non specified, at /adm/login?querystring where 
# querystring contains role=st./$cdom/$cnum
#
                            if ($url eq '') {
                                my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
                                if ($domdefaults{'portal_def'}) {
                                    $url = $domdefaults{'portal_def'};
                                } else {
                                    my $chome = &Apache::lonnet::homeserver($cnum,$cdom);
                                    my $hostname = &Apache::lonnet::hostname($chome);
                                    my $protocol = $Apache::lonnet::protocol{$chome};
                                    $protocol = 'http' if ($protocol ne 'https');
                                    $url = $protocol.'://'.$hostname.'/adm/login?role=st./'.$cdom.'/'.$cnum;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return $url;
}

sub get_student_counts {
    my ($cdom,$cnum) = @_;
    my (%idx,%stucounts);
    my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cnum);
    $idx{'type'} = &Apache::loncoursedata::CL_TYPE();
    $idx{'status'} = &Apache::loncoursedata::CL_STATUS();
    while (my ($student,$data) = each(%$classlist)) {
        if (($data->[$idx{'status'}] eq 'Active') ||
            ($data->[$idx{'status'}] eq 'Future')) {
            if ($data->[$idx{'type'}] eq 'selfenroll') {
                $stucounts{'selfenroll'} ++;
            }
            $stucounts{'allstudents'} ++;
        }
    }
    return (\%stucounts,\%idx,$classlist);
}


=pod

=back

=cut

