Diff for /loncom/lonnet/perl/lonnet.pm between versions 1.1042 and 1.1056.4.11

version 1.1042, 2009/11/06 01:49:10 version 1.1056.4.11, 2010/10/01 14:26:07
Line 76  use HTTP::Date; Line 76  use HTTP::Date;
 use Image::Magick;  use Image::Magick;
   
 use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir  use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir
             $_64bit %env %protocol);              $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease);
   
 my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash,  my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash,
     %userrolehash, $processmarker, $dumpcount, %coursedombuf,      %userrolehash, $processmarker, $dumpcount, %coursedombuf,
Line 99  use LONCAPA::Configuration; Line 99  use LONCAPA::Configuration;
 my $readit;  my $readit;
 my $max_connection_retries = 10;     # Or some such value.  my $max_connection_retries = 10;     # Or some such value.
   
 my $upload_photo_form = 0; #Variable to check  when user upload a photo 0=not 1=true  
   
 require Exporter;  require Exporter;
   
 our @ISA = qw (Exporter);  our @ISA = qw (Exporter);
Line 198  sub get_server_timezone { Line 196  sub get_server_timezone {
 }  }
   
 sub get_server_loncaparev {  sub get_server_loncaparev {
     my ($dom,$lonhost) = @_;      my ($dom,$lonhost,$ignore_cache,$caller) = @_;
     if (defined($lonhost)) {      if (defined($lonhost)) {
         if (!defined(&hostname($lonhost))) {          if (!defined(&hostname($lonhost))) {
             undef($lonhost);              undef($lonhost);
Line 213  sub get_server_loncaparev { Line 211  sub get_server_loncaparev {
         }          }
     }      }
     if (defined($lonhost)) {      if (defined($lonhost)) {
         my $cachetime = 24*3600;          my $cachetime = 12*3600;
         my ($loncaparev,$cached)=&is_cached_new('serverloncaparev',$lonhost);          if (!$ignore_cache) {
               my ($loncaparev,$cached)=&is_cached_new('serverloncaparev',$lonhost);
               if (defined($cached)) {
                   return $loncaparev;
               }
           }
           my ($answer,$loncaparev);
           my @ids=&current_machine_ids();
           if (grep(/^\Q$lonhost\E$/,@ids)) {
               $answer = $perlvar{'lonVersion'};
               if ($answer =~ /^[\'\"]?([\w.\-]+)[\'\"]?$/) {
                   $loncaparev = $1;
               }
           } else {
               $answer = &reply('serverloncaparev',$lonhost);
               if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) {
                   if ($caller eq 'loncron') {
                       my $ua=new LWP::UserAgent;
                       $ua->timeout(4);
                       my $protocol = $protocol{$lonhost};
                       $protocol = 'http' if ($protocol ne 'https');
                       my $url = $protocol.'://'.&hostname($lonhost).'/adm/about.html';
                       my $request=new HTTP::Request('GET',$url);
                       my $response=$ua->request($request);
                       unless ($response->is_error()) {
                           my $content = $response->content;
                           if ($content =~ /<p>VERSION\:\s*([\w.\-]+)<\/p>/) {
                               $loncaparev = $1;
                           }
                       }
                   } else {
                       $loncaparev = $loncaparevs{$lonhost};
                   }
               } elsif ($answer =~ /^[\'\"]?([\w.\-]+)[\'\"]?$/) {
                   $loncaparev = $1;
               }
           }
           return &do_cache_new('serverloncaparev',$lonhost,$loncaparev,$cachetime);
       }
   }
   
   sub get_server_homeID {
       my ($hostname,$ignore_cache,$caller) = @_;
       unless ($ignore_cache) {
           my ($serverhomeID,$cached)=&is_cached_new('serverhomeID',$hostname);
         if (defined($cached)) {          if (defined($cached)) {
             return $loncaparev;              return $serverhomeID;
         } else {  
             my $loncaparev = &reply('serverloncaparev',$lonhost);  
             return &do_cache_new('serverloncaparev',$lonhost,$loncaparev,$cachetime);  
         }          }
     }      }
       my $cachetime = 12*3600;
       my $serverhomeID;
       if ($caller eq 'loncron') {
           my @machine_ids = &machine_ids($hostname);
           foreach my $id (@machine_ids) {
               my $response = &reply('serverhomeID',$id);
               unless (($response eq 'unknown_cmd') || ($response eq 'con_lost')) {
                   $serverhomeID = $response;
                   last;
               }
           }
           if ($serverhomeID eq '') {
               $serverhomeID = $machine_ids[-1];
           }
       } else {
           $serverhomeID = $serverhomeIDs{$hostname};
       }
       return &do_cache_new('serverhomeID',$hostname,$serverhomeID,$cachetime);
 }  }
   
 # -------------------------------------------------- Non-critical communication  # -------------------------------------------------- Non-critical communication
Line 699  sub spareserver { Line 756  sub spareserver {
     if ($userloadpercent !~ /\d/) { $userloadpercent=0; }      if ($userloadpercent !~ /\d/) { $userloadpercent=0; }
     my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent       my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent 
                                                      :  $userloadpercent;                                                       :  $userloadpercent;
           my ($uint_dom,$remotesessions);
       if ($env{'user.domain'}) {
           my $uprimary_id = &Apache::lonnet::domain($env{'user.domain'},'primary');
           $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
           my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
           $remotesessions = $udomdefaults{'remotesessions'};
       }
     foreach my $try_server (@{ $spareid{'primary'} }) {      foreach my $try_server (@{ $spareid{'primary'} }) {
           if ($uint_dom) {
               next unless (&spare_can_host($env{'user.domain'},$uint_dom,
                                            $remotesessions,$try_server));
           }
  ($spare_server, $lowest_load) =   ($spare_server, $lowest_load) =
     &compare_server_load($try_server, $spare_server, $lowest_load);      &compare_server_load($try_server, $spare_server, $lowest_load);
     }      }
Line 709  sub spareserver { Line 776  sub spareserver {
   
     if (!$found_server) {      if (!$found_server) {
  foreach my $try_server (@{ $spareid{'default'} }) {   foreach my $try_server (@{ $spareid{'default'} }) {
               if ($uint_dom) {
                   next unless (&spare_can_host($env{'user.domain'},$uint_dom,
                                                $remotesessions,$try_server));
               }
     ($spare_server, $lowest_load) =      ($spare_server, $lowest_load) =
  &compare_server_load($try_server, $spare_server, $lowest_load);   &compare_server_load($try_server, $spare_server, $lowest_load);
  }   }
Line 721  sub spareserver { Line 792  sub spareserver {
         }          }
         if (defined($spare_server)) {          if (defined($spare_server)) {
             my $hostname = &hostname($spare_server);              my $hostname = &hostname($spare_server);
             if (defined($hostname)) {                if (defined($hostname)) {
         $spare_server = $protocol.'://'.$hostname;          $spare_server = $protocol.'://'.$hostname;
             }              }
         }          }
Line 736  sub compare_server_load { Line 807  sub compare_server_load {
     my $userloadans = &reply('userload',$try_server);      my $userloadans = &reply('userload',$try_server);
   
     if ($loadans !~ /\d/ && $userloadans !~ /\d/) {      if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
  next; #didn't get a number from the server          return; #didn't get a number from the server
     }      }
   
     my $load;      my $load;
Line 779  sub has_user_session { Line 850  sub has_user_session {
     return 0;      return 0;
 }  }
   
   # --------- determine least loaded server in a user's domain which allows login
   
   sub choose_server {
       my ($udom) = @_;
       my %domconfhash = &Apache::loncommon::get_domainconf($udom);
       my %servers = &get_servers($udom);
       my $lowest_load = 30000;
       my ($login_host,$hostname);
       foreach my $lonhost (keys(%servers)) {
           my $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost};
           if ($loginvia eq '') {
               ($login_host, $lowest_load) =
               &compare_server_load($lonhost, $login_host, $lowest_load);
           }
       }
       if ($login_host ne '') {
           $hostname = $servers{$login_host};
       }
       return ($login_host,$hostname);
   }
   
 # --------------------------------------------- Try to change a user's password  # --------------------------------------------- Try to change a user's password
   
 sub changepass {  sub changepass {
Line 837  sub queryauthenticate { Line 929  sub queryauthenticate {
 # --------- Try to authenticate user from domain's lib servers (first this one)  # --------- Try to authenticate user from domain's lib servers (first this one)
   
 sub authenticate {  sub authenticate {
     my ($uname,$upass,$udom,$checkdefauth)=@_;      my ($uname,$upass,$udom,$checkdefauth,$clientcancheckhost)=@_;
     $upass=&escape($upass);      $upass=&escape($upass);
     $uname= &LONCAPA::clean_username($uname);      $uname= &LONCAPA::clean_username($uname);
     my $uhome=&homeserver($uname,$udom,1);      my $uhome=&homeserver($uname,$udom,1);
Line 860  sub authenticate { Line 952  sub authenticate {
     return 'no_host';      return 'no_host';
         }          }
     }      }
     my $answer=reply("encrypt:auth:$udom:$uname:$upass:$checkdefauth",$uhome);      my $answer=reply("encrypt:auth:$udom:$uname:$upass:$checkdefauth:$clientcancheckhost",$uhome);
     if ($answer eq 'authorized') {      if ($answer eq 'authorized') {
         if ($newhome) {          if ($newhome) {
             &logthis("User $uname at $udom authorized by $uhome, but needs account");              &logthis("User $uname at $udom authorized by $uhome, but needs account");
Line 878  sub authenticate { Line 970  sub authenticate {
     return 'no_host';      return 'no_host';
 }  }
   
   sub can_host_session {
       my ($udom,$lonhost,$remoterev,$remotesessions,$hostedsessions) = @_;
       my $canhost = 1;
       my $host_idn = &Apache::lonnet::internet_dom($lonhost);
       if (ref($remotesessions) eq 'HASH') {
           if (ref($remotesessions->{'excludedomain'}) eq 'ARRAY') {
               if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'excludedomain'}})) {
                   $canhost = 0;
               } else {
                   $canhost = 1;
               }
           }
           if (ref($remotesessions->{'includedomain'}) eq 'ARRAY') {
               if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'includedomain'}})) {
                   $canhost = 1;
               } else {
                   $canhost = 0;
               }
           }
           if ($canhost) {
               if ($remotesessions->{'version'} ne '') {
                   my ($reqmajor,$reqminor) = ($remotesessions->{'version'} =~ /^(\d+)\.(\d+)$/);
                   if ($reqmajor ne '' && $reqminor ne '') {
                       if ($remoterev =~ /^\'?(\d+)\.(\d+)/) {
                           my $major = $1;
                           my $minor = $2;
                           if (($major < $reqmajor ) ||
                               (($major == $reqmajor) && ($minor < $reqminor))) {
                               $canhost = 0;
                           }
                       } else {
                           $canhost = 0;
                       }
                   }
               }
           }
       }
       if ($canhost) {
           if (ref($hostedsessions) eq 'HASH') {
               if (ref($hostedsessions->{'excludedomain'}) eq 'ARRAY') {
                   if (grep(/^\Q$udom\E$/,@{$hostedsessions->{'excludedomain'}})) {
                       $canhost = 0;
                   } else {
                       $canhost = 1;
                   }
               }
               if (ref($hostedsessions->{'includedomain'}) eq 'ARRAY') {
                   if (grep(/^\Q$udom\E$/,@{$hostedsessions->{'includedomain'}})) {
                       $canhost = 1;
                   } else {
                       $canhost = 0;
                   }
               }
           }
       }
       return $canhost;
   }
   
   sub spare_can_host {
       my ($udom,$uint_dom,$remotesessions,$try_server)=@_;
       my $canhost=1;
       my @intdoms;
       my $internet_names = &Apache::lonnet::get_internet_names($try_server);
       if (ref($internet_names) eq 'ARRAY') {
           @intdoms = @{$internet_names};
       }
       unless (grep(/^\Q$uint_dom\E$/,@intdoms)) {
           my $serverhomeID = &Apache::lonnet::get_server_homeID($try_server);
           my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
           my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
           my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$try_server);
           $canhost = &can_host_session($udom,$try_server,$remoterev,
                                        $remotesessions,
                                        $defdomdefaults{'hostedsessions'});
       }
       return $canhost;
   }
   
 # ---------------------- Find the homebase for a user from domain's lib servers  # ---------------------- Find the homebase for a user from domain's lib servers
   
 my %homecache;  my %homecache;
Line 1353  sub get_domain_defaults { Line 1523  sub get_domain_defaults {
     my %domdefaults;      my %domdefaults;
     my %domconfig =      my %domconfig =
          &Apache::lonnet::get_dom('configuration',['defaults','quotas',           &Apache::lonnet::get_dom('configuration',['defaults','quotas',
                                   'requestcourses','inststatus'],$domain);                                    'requestcourses','inststatus',
                                     'coursedefaults','usersessions'],$domain);
     if (ref($domconfig{'defaults'}) eq 'HASH') {      if (ref($domconfig{'defaults'}) eq 'HASH') {
         $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'};           $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; 
         $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};          $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};
Line 1388  sub get_domain_defaults { Line 1559  sub get_domain_defaults {
             $domdefaults{$item} = $domconfig{'inststatus'}{$item};              $domdefaults{$item} = $domconfig{'inststatus'}{$item};
         }          }
     }      }
       if (ref($domconfig{'coursedefaults'}) eq 'HASH') {
           foreach my $item ('canuse_pdfforms') {
               $domdefaults{$item} = $domconfig{'coursedefaults'}{$item};
           }
       }
       if (ref($domconfig{'usersessions'}) eq 'HASH') {
           if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
               $domdefaults{'remotesessions'} = $domconfig{'usersessions'}{'remote'};
           }
           if (ref($domconfig{'usersessions'}{'hosted'}) eq 'HASH') {
               $domdefaults{'hostedsessions'} = $domconfig{'usersessions'}{'hosted'};
           }
       }
     &Apache::lonnet::do_cache_new('domdefaults',$domain,\%domdefaults,      &Apache::lonnet::do_cache_new('domdefaults',$domain,\%domdefaults,
                                   $cachetime);                                    $cachetime);
     return %domdefaults;      return %domdefaults;
Line 1573  sub getsection { Line 1757  sub getsection {
     # If there is a role which has expired, return it.      # If there is a role which has expired, return it.
     #      #
     $courseid = &courseid_to_courseurl($courseid);      $courseid = &courseid_to_courseurl($courseid);
     my %roleshash = &dump('roles',$udom,$unam,$courseid);      my $extra = &freeze_escape({'skipcheck' => 1});
       my %roleshash = &dump('roles',$udom,$unam,$courseid,undef,$extra);
     foreach my $key (keys(%roleshash)) {      foreach my $key (keys(%roleshash)) {
         next if ($key !~/^\Q$courseid\E(?:\/)*(\w+)*\_st$/);          next if ($key !~/^\Q$courseid\E(?:\/)*(\w+)*\_st$/);
         my $section=$1;          my $section=$1;
Line 1722  sub userenvironment { Line 1907  sub userenvironment {
     unless ($uhome eq 'no_host') {      unless ($uhome eq 'no_host') {
         my @answer=split(/\&/,           my @answer=split(/\&/, 
             &reply('get:'.$udom.':'.$unam.':environment:'.$items,$uhome));              &reply('get:'.$udom.':'.$unam.':environment:'.$items,$uhome));
           if ($#answer==0 && $answer[0] =~ /^(con_lost|error:|no_such_host)/i) {
               return %returnhash;
           }
         my $i;          my $i;
         for ($i=0;$i<=$#what;$i++) {          for ($i=0;$i<=$#what;$i++) {
     $returnhash{$what[$i]}=&unescape($answer[$i]);      $returnhash{$what[$i]}=&unescape($answer[$i]);
Line 2147  sub clean_filename { Line 2335  sub clean_filename {
     $fname=~s/\.(\d+)(?=\.)/_$1/g;      $fname=~s/\.(\d+)(?=\.)/_$1/g;
     return $fname;      return $fname;
 }  }
 #This Function check if a Image max 400px width and height 500px. If not then scale the image down  # This Function checks if an Image's dimensions exceed either $resizewidth (width) 
   # or $resizeheight (height) - both pixels. If so, the image is scaled to produce an 
   # image with the same aspect ratio as the original, but with dimensions which do 
   # not exceed $resizewidth and $resizeheight.
    
 sub resizeImage {  sub resizeImage {
  my($img_url) = @_;      my ($img_path,$resizewidth,$resizeheight) = @_;
  my $ima = Image::Magick->new;                             my $ima = Image::Magick->new;
         $ima->Read($img_url);      my $resized;
  if($ima->Get('width') > 400)      if (-e $img_path) {
  {          $ima->Read($img_path);
  my $factor = $ima->Get('width')/400;          if (($resizewidth =~ /^\d+$/) && ($resizeheight > 0)) {
               $ima->Scale( width=>400, height=>$ima->Get('height')/$factor );              my $width = $ima->Get('width');
  }              my $height = $ima->Get('height');
  if($ima->Get('height') > 500)              if ($width > $resizewidth) {
         {          my $factor = $width/$resizewidth;
         my $factor = $ima->Get('height')/500;                  my $newheight = $height/$factor;
                 $ima->Scale( width=>$ima->Get('width')/$factor, height=>500);                  $ima->Scale(width=>$resizewidth,height=>$newheight);
         }                   $resized = 1;
               }
  $ima->Write($img_url);          }
 }          if (($resizeheight =~ /^\d+$/) && ($resizeheight > 0)) {
               my $width = $ima->Get('width');
 #Wrapper function for userphotoupload              my $height = $ima->Get('height');
 sub userphotoupload              if ($height > $resizeheight) {
 {                  my $factor = $height/$resizeheight;
  my($formname,$subdir) = @_;                  my $newwidth = $width/$factor;
  $upload_photo_form = 1;                  $ima->Scale(width=>$newwidth,height=>$resizeheight);
  return &userfileupload($formname,undef,$subdir);                  $resized = 1;
               }
           }
           if ($resized) {
               $ima->Write($img_path);
           }
       }
       return;
 }  }
   
 # --------------- Take an uploaded file and put it into the userfiles directory  # --------------- Take an uploaded file and put it into the userfiles directory
Line 2187  sub userphotoupload Line 2386  sub userphotoupload
 #        $dsetudom - domain for permanaent storage of uploaded file  #        $dsetudom - domain for permanaent storage of uploaded file
 #        $thumbwidth - width (pixels) of thumbnail to make for uploaded image   #        $thumbwidth - width (pixels) of thumbnail to make for uploaded image 
 #        $thumbheight - height (pixels) of thumbnail to make for uploaded image  #        $thumbheight - height (pixels) of thumbnail to make for uploaded image
   #        $resizewidth - width (pixels) to which to resize uploaded image
   #        $resizeheight - height (pixels) to which to resize uploaded image
 #   # 
 # output: url of file in userspace, or error: <message>   # output: url of file in userspace, or error: <message> 
 #             or /adm/notfound.html if failure to upload occurse  #             or /adm/notfound.html if failure to upload occurse
   
   
 sub userfileupload {  sub userfileupload {
     my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase,$destuname,      my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase,$destuname,
         $destudom,$thumbwidth,$thumbheight)=@_;          $destudom,$thumbwidth,$thumbheight,$resizewidth,$resizeheight)=@_;
     if (!defined($subdir)) { $subdir='unknown'; }      if (!defined($subdir)) { $subdir='unknown'; }
     my $fname=$env{'form.'.$formname.'.filename'};      my $fname=$env{'form.'.$formname.'.filename'};
     $fname=&clean_filename($fname);      $fname=&clean_filename($fname);
Line 2244  sub userfileupload { Line 2444  sub userfileupload {
         if ($env{'form.folder'} =~ m/^(default|supplemental)/) {          if ($env{'form.folder'} =~ m/^(default|supplemental)/) {
             return &finishuserfileupload($docuname,$docudom,              return &finishuserfileupload($docuname,$docudom,
  $formname,$fname,$parser,$allfiles,   $formname,$fname,$parser,$allfiles,
  $codebase,$thumbwidth,$thumbheight);   $codebase,$thumbwidth,$thumbheight,
                                            $resizewidth,$resizeheight);
         } else {          } else {
             $fname=$env{'form.folder'}.'/'.$fname;              $fname=$env{'form.folder'}.'/'.$fname;
             return &process_coursefile('uploaddoc',$docuname,$docudom,              return &process_coursefile('uploaddoc',$docuname,$docudom,
Line 2256  sub userfileupload { Line 2457  sub userfileupload {
         my $docudom=$destudom;          my $docudom=$destudom;
  return &finishuserfileupload($docuname,$docudom,$formname,$fname,   return &finishuserfileupload($docuname,$docudom,$formname,$fname,
      $parser,$allfiles,$codebase,       $parser,$allfiles,$codebase,
                                      $thumbwidth,$thumbheight);                                       $thumbwidth,$thumbheight,
                                        $resizewidth,$resizeheight);
                   
     } else {      } else {
         my $docuname=$env{'user.name'};          my $docuname=$env{'user.name'};
Line 2267  sub userfileupload { Line 2469  sub userfileupload {
         }          }
  return &finishuserfileupload($docuname,$docudom,$formname,$fname,   return &finishuserfileupload($docuname,$docudom,$formname,$fname,
      $parser,$allfiles,$codebase,       $parser,$allfiles,$codebase,
                                      $thumbwidth,$thumbheight);                                       $thumbwidth,$thumbheight,
                                        $resizewidth,$resizeheight);
     }      }
 }  }
   
 sub finishuserfileupload {  sub finishuserfileupload {
     my ($docuname,$docudom,$formname,$fname,$parser,$allfiles,$codebase,      my ($docuname,$docudom,$formname,$fname,$parser,$allfiles,$codebase,
         $thumbwidth,$thumbheight) = @_;          $thumbwidth,$thumbheight,$resizewidth,$resizeheight) = @_;
     my $path=$docudom.'/'.$docuname.'/';      my $path=$docudom.'/'.$docuname.'/';
     my $filepath=$perlvar{'lonDocRoot'};      my $filepath=$perlvar{'lonDocRoot'};
       
Line 2305  sub finishuserfileupload { Line 2508  sub finishuserfileupload {
     return '/adm/notfound.html';      return '/adm/notfound.html';
  }   }
  close(FH);   close(FH);
  if($upload_photo_form==1)          if ($resizewidth && $resizeheight) {
  {              my $mm = new File::MMagic;
  resizeImage($filepath.'/'.$file);              my $mime_type = $mm->checktype_filename($filepath.'/'.$file);
  $upload_photo_form = 0;              if ($mime_type =~ m{^image/}) {
           &resizeImage($filepath.'/'.$file,$resizewidth,$resizeheight);
               }  
  }   }
     }      }
     if ($parser eq 'parse') {      if ($parser eq 'parse') {
Line 2760  sub courserolelog { Line 2965  sub courserolelog {
     if (($trole eq 'cc') || ($trole eq 'in') ||      if (($trole eq 'cc') || ($trole eq 'in') ||
         ($trole eq 'ep') || ($trole eq 'ad') ||          ($trole eq 'ep') || ($trole eq 'ad') ||
         ($trole eq 'ta') || ($trole eq 'st') ||          ($trole eq 'ta') || ($trole eq 'st') ||
         ($trole=~/^cr/) || ($trole eq 'gr')) {          ($trole=~/^cr/) || ($trole eq 'gr') ||
           ($trole eq 'co')) {
         if ($area =~ m-^/($match_domain)/($match_courseid)/?([^/]*)-) {          if ($area =~ m-^/($match_domain)/($match_courseid)/?([^/]*)-) {
             my $cdom = $1;              my $cdom = $1;
             my $cnum = $2;              my $cnum = $2;
Line 2855  sub get_my_roles { Line 3061  sub get_my_roles {
     unless (defined($udom)) { $udom=$env{'user.domain'}; }      unless (defined($udom)) { $udom=$env{'user.domain'}; }
     my (%dumphash,%nothide);      my (%dumphash,%nothide);
     if ($context eq 'userroles') {       if ($context eq 'userroles') { 
         %dumphash = &dump('roles',$udom,$uname);          my $extra = &freeze_escape({'skipcheck' => 1});
           %dumphash = &dump('roles',$udom,$uname,'.',undef,$extra);
     } else {      } else {
         %dumphash=          %dumphash=
             &dump('nohist_userroles',$udom,$uname);              &dump('nohist_userroles',$udom,$uname);
Line 2994  sub getannounce { Line 3201  sub getannounce {
   
 sub courseidput {  sub courseidput {
     my ($domain,$storehash,$coursehome,$caller) = @_;      my ($domain,$storehash,$coursehome,$caller) = @_;
       return unless (ref($storehash) eq 'HASH');
     my $outcome;      my $outcome;
     if ($caller eq 'timeonly') {      if ($caller eq 'timeonly') {
         my $cids = '';          my $cids = '';
Line 3033  sub courseiddump { Line 3241  sub courseiddump {
     my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,      my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,
         $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok,          $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok,
         $selfenrollonly,$catfilter,$showhidden,$caller,$cloner,$cc_clone,          $selfenrollonly,$catfilter,$showhidden,$caller,$cloner,$cc_clone,
         $cloneonly,$createdbefore,$createdafter,$creationcontext)=@_;          $cloneonly,$createdbefore,$createdafter,$creationcontext,$domcloner)=@_;
     my $as_hash = 1;      my $as_hash = 1;
     my %returnhash;      my %returnhash;
     if (!$domfilter) { $domfilter=''; }      if (!$domfilter) { $domfilter=''; }
Line 3055  sub courseiddump { Line 3263  sub courseiddump {
                          $showhidden.':'.$caller.':'.&escape($cloner).':'.                           $showhidden.':'.$caller.':'.&escape($cloner).':'.
                          &escape($cc_clone).':'.$cloneonly.':'.                           &escape($cc_clone).':'.$cloneonly.':'.
                          &escape($createdbefore).':'.&escape($createdafter).':'.                           &escape($createdbefore).':'.&escape($createdafter).':'.
                          &escape($creationcontext),$tryserver);                           &escape($creationcontext).':'.$domcloner,
                            $tryserver);
                 my @pairs=split(/\&/,$rep);                  my @pairs=split(/\&/,$rep);
                 foreach my $item (@pairs) {                  foreach my $item (@pairs) {
                     my ($key,$value)=split(/\=/,$item,2);                      my ($key,$value)=split(/\=/,$item,2);
Line 3078  sub courseiddump { Line 3287  sub courseiddump {
     return %returnhash;      return %returnhash;
 }  }
   
   sub courselastaccess {
       my ($cdom,$cnum,$hostidref) = @_;
       my %returnhash;
       if ($cdom && $cnum) {
           my $chome = &homeserver($cnum,$cdom);
           if ($chome ne 'no_host') {
               my $rep = &reply('courselastaccess:'.$cdom.':'.$cnum,$chome);
               &extract_lastaccess(\%returnhash,$rep);
           }
       } else {
           if (!$cdom) { $cdom=''; }
           my %libserv = &all_library();
           foreach my $tryserver (keys(%libserv)) {
               if (ref($hostidref) eq 'ARRAY') {
                   next unless (grep(/^\Q$tryserver\E$/,@{$hostidref}));
               } 
               if (($cdom eq '') || (&host_domain($tryserver) eq $cdom)) {
                   my $rep = &reply('courselastaccess:'.&host_domain($tryserver).':',$tryserver);
                   &extract_lastaccess(\%returnhash,$rep);
               }
           }
       }
       return %returnhash;
   }
   
   sub extract_lastaccess {
       my ($returnhash,$rep) = @_;
       if (ref($returnhash) eq 'HASH') {
           unless ($rep eq 'unknown_command' || $rep eq 'no_such_host' || 
                   $rep eq 'con_lost' || $rep eq 'rejected' || $rep eq 'refused' ||
                    $rep eq '') {
               my @pairs=split(/\&/,$rep);
               foreach my $item (@pairs) {
                   my ($key,$value)=split(/\=/,$item,2);
                   $key = &unescape($key);
                   next if ($key =~ /^error: 2 /);
                   $returnhash->{$key} = &thaw_unescape($value);
               }
           }
       }
       return;
   }
   
 # ---------------------------------------------------------- DC e-mail  # ---------------------------------------------------------- DC e-mail
   
 sub dcmailput {  sub dcmailput {
Line 3797  sub coursedescription { Line 4049  sub coursedescription {
     return %returnhash;      return %returnhash;
 }  }
   
   sub update_released_required {
       my ($needsrelease,$cdom,$cnum,$chome,$cid) = @_;
       if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
           $cid = $env{'request.course.id'};
           $cdom = $env{'course.'.$cid.'.domain'};
           $cnum = $env{'course.'.$cid.'.num'};
           $chome = $env{'course.'.$cid.'.home'};
       }
       if ($needsrelease) {
           my %curr_reqd_hash = &userenvironment($cdom,$cnum,'internal.releaserequired');
           my $needsupdate;
           if ($curr_reqd_hash{'internal.releaserequired'} eq '') {
               $needsupdate = 1;
           } else {
               my ($currmajor,$currminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
               my ($needsmajor,$needsminor) = split(/\./,$needsrelease);
               if (($currmajor < $needsmajor) || ($currmajor == $needsmajor && $currminor < $needsminor)) {
                   $needsupdate = 1;
               }
           }
           if ($needsupdate) {
               my %needshash = (
                                'internal.releaserequired' => $needsrelease,
                               );
               my $putresult = &put('environment',\%needshash,$cdom,$cnum);
               if ($putresult eq 'ok') {
                   &appenv({'course.'.$cid.'.internal.releaserequired' => $needsrelease});
                   my %crsinfo = &courseiddump($cdom,'.',1,'.','.',$cnum,undef,undef,'.');
                   if (ref($crsinfo{$cid}) eq 'HASH') {
                       $crsinfo{$cid}{'releaserequired'} = $needsrelease;
                       &courseidput($cdom,\%crsinfo,$chome,'notime');
                   }
               }
           }
       }
       return;
   }
   
 # -------------------------------------------------See if a user is privileged  # -------------------------------------------------See if a user is privileged
   
 sub privileged {  sub privileged {
Line 3836  sub rolesinit { Line 4126  sub rolesinit {
     my ($domain,$username,$authhost)=@_;      my ($domain,$username,$authhost)=@_;
     my $now=time;      my $now=time;
     my %userroles = ('user.login.time' => $now);      my %userroles = ('user.login.time' => $now);
     my $rolesdump=reply("dump:$domain:$username:roles",$authhost);      my $extra = &freeze_escape({'skipcheck' => 1});
       my $rolesdump=reply("dump:$domain:$username:roles:.::$extra",$authhost);
     if (($rolesdump eq 'con_lost') || ($rolesdump eq '') ||       if (($rolesdump eq 'con_lost') || ($rolesdump eq '') || 
         ($rolesdump =~ /^error:/)) {           ($rolesdump =~ /^error:/)) {
         return \%userroles;          return \%userroles;
     }      }
     my %allroles=();      my %allroles=();
Line 3908  sub custom_roleprivs { Line 4199  sub custom_roleprivs {
         if (($rdummy ne 'con_lost') && ($roledef ne '')) {          if (($rdummy ne 'con_lost') && ($roledef ne '')) {
             my ($syspriv,$dompriv,$coursepriv)=split(/\_/,$roledef);              my ($syspriv,$dompriv,$coursepriv)=split(/\_/,$roledef);
             if (defined($syspriv)) {              if (defined($syspriv)) {
                   if ($trest =~ /^$match_community$/) {
                       $syspriv =~ s/bre\&S//; 
                   }
                 $$allroles{'cm./'}.=':'.$syspriv;                  $$allroles{'cm./'}.=':'.$syspriv;
                 $$allroles{$spec.'./'}.=':'.$syspriv;                  $$allroles{$spec.'./'}.=':'.$syspriv;
             }              }
Line 3956  sub standard_roleprivs { Line 4250  sub standard_roleprivs {
 }  }
   
 sub set_userprivs {  sub set_userprivs {
     my ($userroles,$allroles,$allgroups) = @_;       my ($userroles,$allroles,$allgroups,$groups_roles) = @_; 
     my $author=0;      my $author=0;
     my $adv=0;      my $adv=0;
     my %grouproles = ();      my %grouproles = ();
     if (keys(%{$allgroups}) > 0) {      if (keys(%{$allgroups}) > 0) {
           my @groupkeys;
         foreach my $role (keys(%{$allroles})) {          foreach my $role (keys(%{$allroles})) {
             my ($trole,$area,$sec,$extendedarea);              push(@groupkeys,$role);
             if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)\.-) {          }
                 $trole = $1;          if (ref($groups_roles) eq 'HASH') {
                 $area = $2;              foreach my $key (keys(%{$groups_roles})) {
                 $sec = $3;                  unless (grep(/^\Q$key\E$/,@groupkeys)) {
                 $extendedarea = $area.$sec;                      push(@groupkeys,$key);
                 if (exists($$allgroups{$area})) {                  }
                     foreach my $group (keys(%{$$allgroups{$area}})) {              }
                         my $spec = $trole.'.'.$extendedarea;          }
                         $grouproles{$spec.'.'.$area.'/'.$group} =           if (@groupkeys > 0) {
               foreach my $role (@groupkeys) {
                   my ($trole,$area,$sec,$extendedarea);
                   if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)\.-) {
                       $trole = $1;
                       $area = $2;
                       $sec = $3;
                       $extendedarea = $area.$sec;
                       if (exists($$allgroups{$area})) {
                           foreach my $group (keys(%{$$allgroups{$area}})) {
                               my $spec = $trole.'.'.$extendedarea;
                               $grouproles{$spec.'.'.$area.'/'.$group} = 
                                                 $$allgroups{$area}{$group};                                                  $$allgroups{$area}{$group};
                           }
                     }                      }
                 }                  }
             }              }
Line 4019  sub role_status { Line 4326  sub role_status {
                 if ($$tstart<$now) {                  if ($$tstart<$now) {
                     if ($$tstart && $$tstart>$refresh) {                      if ($$tstart && $$tstart>$refresh) {
                         if (($$where ne '') && ($$role ne '')) {                          if (($$where ne '') && ($$role ne '')) {
                             my (%allroles,%allgroups,$group_privs);                              my (%allroles,%allgroups,$group_privs,
                                   %groups_roles,@rolecodes);
                             my %userroles = (                              my %userroles = (
                                 'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend                                  'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend
                             );                              );
                               @rolecodes = ('cm');
                             my $spec=$$role.'.'.$$where;                              my $spec=$$role.'.'.$$where;
                             my ($tdummy,$tdomain,$trest)=split(/\//,$$where);                              my ($tdummy,$tdomain,$trest)=split(/\//,$$where);
                             if ($$role eq 'gr') {  
                                 my %rolehash = &get('roles',[$$where.'_'.$$role],$env{'user.domain'},  
                                                     $env{'user.name'})=@_;  
                                 my ($trole) = split('_',$role,1);  
                                 (undef,my $group_privs) = split(/\//,$trole);  
                                 $group_privs = &unescape($group_privs);  
                             }  
                             if ($$role =~ /^cr\//) {                              if ($$role =~ /^cr\//) {
                                 &custom_roleprivs(\%allroles,$$role,$tdomain,$trest,$spec,$$where);                                  &custom_roleprivs(\%allroles,$$role,$tdomain,$trest,$spec,$$where);
                                   push(@rolecodes,'cr');
                             } elsif ($$role eq 'gr') {                              } elsif ($$role eq 'gr') {
                                   push(@rolecodes,$$role);
                                 my %rolehash = &get('roles',[$$where.'_'.$$role],$env{'user.domain'},                                  my %rolehash = &get('roles',[$$where.'_'.$$role],$env{'user.domain'},
                                                     $env{'user.name'});                                                      $env{'user.name'});
                                 my $trole = split('_',$rolehash{$$where.'_'.$$role},1);                                  my ($trole) = split('_',$rolehash{$$where.'_'.$$role},2);
                                 (undef,my $group_privs) = split(/\//,$trole);                                  (undef,my $group_privs) = split(/\//,$trole);
                                 $group_privs = &unescape($group_privs);                                  $group_privs = &unescape($group_privs);
                                 &group_roleprivs(\%allgroups,$$where,$group_privs,$$tend,$$tstart);                                  &group_roleprivs(\%allgroups,$$where,$group_privs,$$tend,$$tstart);
                                   my %course_roles = &get_my_roles($env{'user.name'},$env{'user.domain'},'userroles',['active'],['cc','co','in','ta','ep','ad','st','cr'],[$tdomain],1);
                                   if (keys(%course_roles) > 0) {
                                       my ($tnum) = ($trest =~ /^($match_courseid)/);
                                       if ($tdomain ne '' && $tnum ne '') {
                                           foreach my $key (keys(%course_roles)) {
                                               if ($key =~ /^\Q$tnum\E:\Q$tdomain\E:([^:]+):?([^:]*)/) {
                                                   my $crsrole = $1;
                                                   my $crssec = $2;
                                                   if ($crsrole =~ /^cr/) {
                                                       unless (grep(/^cr$/,@rolecodes)) {
                                                           push(@rolecodes,'cr');
                                                       }
                                                   } else {
                                                       unless(grep(/^\Q$crsrole\E$/,@rolecodes)) {
                                                           push(@rolecodes,$crsrole);
                                                       }
                                                   }
                                                   my $rolekey = $crsrole.'./'.$tdomain.'/'.$tnum;
                                                   if ($crssec ne '') {
                                                       $rolekey .= '/'.$crssec;
                                                   }
                                                   $rolekey .= './';
                                                   $groups_roles{$rolekey} = \@rolecodes;
                                               }
                                           }
                                       }
                                   }
                             } else {                              } else {
                                   push(@rolecodes,$$role);
                                 &standard_roleprivs(\%allroles,$$role,$tdomain,$spec,$trest,$$where);                                  &standard_roleprivs(\%allroles,$$role,$tdomain,$spec,$trest,$$where);
                             }                              }
                             my ($author,$adv)= &set_userprivs(\%userroles,\%allroles,\%allgroups);                              my ($author,$adv)= &set_userprivs(\%userroles,\%allroles,\%allgroups,\%groups_roles);
                             &appenv(\%userroles,[$$role,'cm']);                              &appenv(\%userroles,\@rolecodes);
                             &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role);                              &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role);
                         }                          }
                     }                      }
Line 4145  sub del { Line 4477  sub del {
 # -------------------------------------------------------------- dump interface  # -------------------------------------------------------------- dump interface
   
 sub dump {  sub dump {
     my ($namespace,$udomain,$uname,$regexp,$range)=@_;      my ($namespace,$udomain,$uname,$regexp,$range,$extra)=@_;
     if (!$udomain) { $udomain=$env{'user.domain'}; }      if (!$udomain) { $udomain=$env{'user.domain'}; }
     if (!$uname) { $uname=$env{'user.name'}; }      if (!$uname) { $uname=$env{'user.name'}; }
     my $uhome=&homeserver($uname,$udomain);      my $uhome=&homeserver($uname,$udomain);
Line 4154  sub dump { Line 4486  sub dump {
     } else {      } else {
  $regexp='.';   $regexp='.';
     }      }
     my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);      my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range:$extra",$uhome);
     my @pairs=split(/\&/,$rep);      my @pairs=split(/\&/,$rep);
     my %returnhash=();      my %returnhash=();
     foreach my $item (@pairs) {      foreach my $item (@pairs) {
Line 4559  sub get_portfolio_access { Line 4891  sub get_portfolio_access {
                 my (%allgroups,%allroles);                   my (%allgroups,%allroles); 
                 my ($start,$end,$role,$sec,$group);                  my ($start,$end,$role,$sec,$group);
                 foreach my $envkey (%env) {                  foreach my $envkey (%env) {
                     if ($envkey =~ m-^user\.role\.(gr|cc|in|ta|ep|st)\./($match_domain)/($match_courseid)/?([^/]*)$-) {                      if ($envkey =~ m-^user\.role\.(gr|cc|co|in|ta|ep|ad|st)\./($match_domain)/($match_courseid)/?([^/]*)$-) {
                         my $cid = $2.'_'.$3;                           my $cid = $2.'_'.$3; 
                         if ($1 eq 'gr') {                          if ($1 eq 'gr') {
                             $group = $4;                              $group = $4;
Line 4699  sub is_portfolio_file { Line 5031  sub is_portfolio_file {
 }  }
   
 sub usertools_access {  sub usertools_access {
     my ($uname,$udom,$tool,$action,$context) = @_;      my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref) = @_;
     my ($access,%tools);      my ($access,%tools);
     if ($context eq '') {      if ($context eq '') {
         $context = 'tools';          $context = 'tools';
Line 4741  sub usertools_access { Line 5073  sub usertools_access {
         $toolstatus = $env{'environment.'.$context.'.'.$tool};          $toolstatus = $env{'environment.'.$context.'.'.$tool};
         $inststatus = $env{'environment.inststatus'};          $inststatus = $env{'environment.inststatus'};
     } else {      } else {
         my %userenv = &userenvironment($udom,$uname,$context.'.'.$tool,'inststatus');          if (ref($userenvref) eq 'HASH') {
         $toolstatus = $userenv{$context.'.'.$tool};              $toolstatus = $userenvref->{$context.'.'.$tool};
         $inststatus = $userenv{'inststatus'};              $inststatus = $userenvref->{'inststatus'};
           } else {
               my %userenv = &userenvironment($udom,$uname,$context.'.'.$tool,'inststatus');
               $toolstatus = $userenv{$context.'.'.$tool};
               $inststatus = $userenv{'inststatus'};
           }
     }      }
   
     if ($toolstatus ne '') {      if ($toolstatus ne '') {
Line 4755  sub usertools_access { Line 5092  sub usertools_access {
         return $access;          return $access;
     }      }
   
     my $is_adv = &is_advanced_user($udom,$uname);      my ($is_adv,%domdef);
     my %domdef = &get_domain_defaults($udom);      if (ref($is_advref) eq 'HASH') {
           $is_adv = $is_advref->{'is_adv'};
       } else {
           $is_adv = &is_advanced_user($udom,$uname);
       }
       if (ref($domdefref) eq 'HASH') {
           %domdef = %{$domdefref};
       } else {
           %domdef = &get_domain_defaults($udom);
       }
     if (ref($domdef{$tool}) eq 'HASH') {      if (ref($domdef{$tool}) eq 'HASH') {
         if ($is_adv) {          if ($is_adv) {
             if ($domdef{$tool}{'_LC_adv'} ne '') {              if ($domdef{$tool}{'_LC_adv'} ne '') {
Line 4807  sub usertools_access { Line 5153  sub usertools_access {
     }      }
 }  }
   
   sub is_course_owner {
       my ($cdom,$cnum,$udom,$uname) = @_;
       if (($udom eq '') || ($uname eq '')) {
           $udom = $env{'user.domain'};
           $uname = $env{'user.name'};
       }
       unless (($udom eq '') || ($uname eq '')) {
           if (exists($env{'course.'.$cdom.'_'.$cnum.'.internal.courseowner'})) {
               if ($env{'course.'.$cdom.'_'.$cnum.'.internal.courseowner'} eq $uname.':'.$udom) {
                   return 1;
               } else {
                   my %courseinfo = &Apache::lonnet::coursedescription($cdom.'/'.$cnum);
                   if ($courseinfo{'internal.courseowner'} eq $uname.':'.$udom) {
                       return 1;
                   }
               }
           }
       }
       return;
   }
   
 sub is_advanced_user {  sub is_advanced_user {
     my ($udom,$uname) = @_;      my ($udom,$uname) = @_;
       if ($udom ne '' && $uname ne '') {
           if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
               return $env{'user.adv'};
           }
       }
     my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);      my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);
     my %allroles;      my %allroles;
     my $is_adv;      my $is_adv;
Line 5046  sub allowed { Line 5418  sub allowed {
     my $courseprivid='';      my $courseprivid='';
   
     my $ownaccess;      my $ownaccess;
     # Community Coordinator browsing resource space.      # Community Coordinator or Assistant Co-author browsing resource space.
     if (($priv eq 'bro') && ($env{'user.author'})) {      if (($priv eq 'bro') && ($env{'user.author'})) {
         if ($uri eq '') {          if ($uri eq '') {
             $ownaccess = 1;              $ownaccess = 1;
Line 5095  sub allowed { Line 5467  sub allowed {
 # Course  # Course
   
     if ($env{'user.priv.'.$env{'request.role'}.'./'}=~/\Q$priv\E\&([^\:]*)/) {      if ($env{'user.priv.'.$env{'request.role'}.'./'}=~/\Q$priv\E\&([^\:]*)/) {
         unless (($priv eq 'bro' && !$ownaccess)) {          unless (($priv eq 'bro') && (!$ownaccess)) {
             $thisallowed.=$1;              $thisallowed.=$1;
         }          }
     }      }
Line 5104  sub allowed { Line 5476  sub allowed {
   
     if ($env{'user.priv.'.$env{'request.role'}.'./'.(split(/\//,$uri))[0].'/'}      if ($env{'user.priv.'.$env{'request.role'}.'./'.(split(/\//,$uri))[0].'/'}
        =~/\Q$priv\E\&([^\:]*)/) {         =~/\Q$priv\E\&([^\:]*)/) {
         unless (($priv eq 'bro' && !$ownaccess)) {          unless (($priv eq 'bro') && (!$ownaccess)) {
             $thisallowed.=$1;              $thisallowed.=$1;
         }          }
     }      }
Line 5116  sub allowed { Line 5488  sub allowed {
   
     if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseuri}      if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseuri}
        =~/\Q$priv\E\&([^\:]*)/) {         =~/\Q$priv\E\&([^\:]*)/) {
         unless (($priv eq 'bro' && !$ownaccess)) {          unless (($priv eq 'bro') && (!$ownaccess)) {
             $thisallowed.=$1;              $thisallowed.=$1;
         }          }
     }      }
Line 5531  sub update_allusers_table { Line 5903  sub update_allusers_table {
                'generation='.&escape($names->{'generation'}).'%%'.                 'generation='.&escape($names->{'generation'}).'%%'.
                'permanentemail='.&escape($names->{'permanentemail'}).'%%'.                 'permanentemail='.&escape($names->{'permanentemail'}).'%%'.
                'id='.&escape($names->{'id'}),$homeserver);                 'id='.&escape($names->{'id'}),$homeserver);
     my $reply = &get_query_reply($queryid);      return;
     return $reply;  
 }  }
   
 # ------- Request retrieval of institutional classlists for course(s)  # ------- Request retrieval of institutional classlists for course(s)
Line 5726  sub auto_validate_instcode { Line 6097  sub auto_validate_instcode {
             $homeserver = &domain($cdom,'primary');              $homeserver = &domain($cdom,'primary');
         }          }
     }      }
     my $response=&unescape(&reply('autovalidateinstcode:'.$cdom.':'.      $response=&unescape(&reply('autovalidateinstcode:'.$cdom.':'.
                            &escape($instcode).':'.&escape($owner),$homeserver));                          &escape($instcode).':'.&escape($owner),$homeserver));
     my ($outcome,$description) = map { &unescape($_); } split('&',$response,2);      my ($outcome,$description) = map { &unescape($_); } split('&',$response,2);
     return ($outcome,$description);      return ($outcome,$description);
 }  }
Line 6095  sub get_users_groups { Line 6466  sub get_users_groups {
     } else {        } else {  
         $grouplist = '';          $grouplist = '';
         my $courseurl = &courseid_to_courseurl($courseid);          my $courseurl = &courseid_to_courseurl($courseid);
         my %roleshash = &dump('roles',$udom,$uname,$courseurl);          my $extra = &freeze_escape({'skipcheck' => 1});
           my %roleshash = &dump('roles',$udom,$uname,$courseurl,undef,$extra);
         my $access_end = $env{'course.'.$courseid.          my $access_end = $env{'course.'.$courseid.
                               '.default_enrollment_end_date'};                                '.default_enrollment_end_date'};
         my $now = time;          my $now = time;
Line 6138  sub devalidate_getgroups_cache { Line 6510  sub devalidate_getgroups_cache {
   
 sub plaintext {  sub plaintext {
     my ($short,$type,$cid,$forcedefault) = @_;      my ($short,$type,$cid,$forcedefault) = @_;
     if ($short =~ /^cr/) {      if ($short =~ m{^cr/}) {
  return (split('/',$short))[-1];   return (split('/',$short))[-1];
     }      }
     if (!defined($cid)) {      if (!defined($cid)) {
Line 6227  sub assignrole { Line 6599  sub assignrole {
                 $refused = 1;                  $refused = 1;
             }              }
             if ($refused) {              if ($refused) {
                 if (($selfenroll == 1) && ($role eq 'st') && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {                  my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
                   if (!$selfenroll && $context eq 'course') {
                       my %crsenv;
                       if ($role eq 'cc' || $role eq 'co') {
                           %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));
                           if (($role eq 'cc') && ($cnum !~ /^$match_community$/)) {
                               if ($env{'request.role'} eq 'cc./'.$cdom.'/'.$cnum) {
                                   if ($crsenv{'internal.courseowner'} eq 
                                       $env{'user.name'}.':'.$env{'user.domain'}) {
                                       $refused = '';
                                   }
                               }
                           } elsif (($role eq 'co') && ($cnum =~ /^$match_community$/)) { 
                               if ($env{'request.role'} eq 'co./'.$cdom.'/'.$cnum) {
                                   if ($crsenv{'internal.courseowner'} eq 
                                       $env{'user.name'}.':'.$env{'user.domain'}) {
                                       $refused = '';
                                   }
                               }
                           }
                       }
                   } elsif (($selfenroll == 1) && ($role eq 'st') && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
                     $refused = '';                      $refused = '';
                 } elsif ($context eq 'requestcourses') {                  } elsif ($context eq 'requestcourses') {
                     my @possroles = ('st','ta','ep','in','cc','co');                      my @possroles = ('st','ta','ep','in','cc','co');
                     if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) {                      if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) {
                         my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});  
                         my $wrongcc;                          my $wrongcc;
                         if ($cnum =~ /^$match_community$/) {                          if ($cnum =~ /^$match_community$/) {
                             $wrongcc = 1 if ($role eq 'cc');                              $wrongcc = 1 if ($role eq 'cc');
Line 6295  sub assignrole { Line 6687  sub assignrole {
             &Apache::longroup::group_changes($udom,$uname,$url,$role,$origend,              &Apache::longroup::group_changes($udom,$uname,$url,$role,$origend,
                                              $origstart,$selfenroll,$context);                                               $origstart,$selfenroll,$context);
         }          }
           if ($role eq 'cc') {
               &autoupdate_coowners($url,$end,$start,$uname,$udom);
           }
     }      }
     return $answer;      return $answer;
 }  }
   
   sub autoupdate_coowners {
       my ($url,$end,$start,$uname,$udom) = @_;
       my ($cdom,$cnum) = ($url =~ m{^/($match_domain)/($match_courseid)});
       if (($cdom ne '') && ($cnum ne '')) {
           my $now = time;
           my %domdesign = &Apache::loncommon::get_domainconf($cdom);
           if ($domdesign{$cdom.'.autoassign.co-owners'}) {
               my %coursehash = &coursedescription($cdom.'_'.$cnum);
               my $instcode = $coursehash{'internal.coursecode'};
               if ($instcode ne '') {
                   if (($start && $start <= $now) && ($end == 0) || ($end > $now)) {
                       unless ($coursehash{'internal.courseowner'} eq $uname.':'.$udom) {
                           my ($delcoowners,@newcoowners,$putresult,$delresult,$coowners);
                           my ($result,$desc) = &auto_validate_instcode($cnum,$cdom,$instcode,$uname.':'.$udom);
                           if ($result eq 'valid') {
                               if ($coursehash{'internal.co-owners'}) {
                                   foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) {
                                       push(@newcoowners,$coowner);
                                   }
                                   unless (grep(/^\Q$uname\E:\Q$udom\E$/,@newcoowners)) {
                                       push(@newcoowners,$uname.':'.$udom);
                                   }
                                   @newcoowners = sort(@newcoowners);
                               } else {
                                   push(@newcoowners,$uname.':'.$udom);
                               }
                           } else {
                               if ($coursehash{'internal.co-owners'}) {
                                   foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) {
                                       unless ($coowner eq $uname.':'.$udom) {
                                           push(@newcoowners,$coowner);
                                       }
                                   }
                                   unless (@newcoowners > 0) {
                                       $delcoowners = 1;
                                       $coowners = '';
                                   }
                               }
                           }
                           if (@newcoowners || $delcoowners) {
                               &store_coowners($cdom,$cnum,$coursehash{'home'},
                                               $delcoowners,@newcoowners);
                           }
                       }
                   }
               }
           }
       }
   }
   
   sub store_coowners {
       my ($cdom,$cnum,$chome,$delcoowners,@newcoowners) = @_;
       my $cid = $cdom.'_'.$cnum;
       my ($coowners,$delresult,$putresult);
       if (@newcoowners) {
           $coowners = join(',',@newcoowners);
           my %coownershash = (
                               'internal.co-owners' => $coowners,
                              );
           $putresult = &put('environment',\%coownershash,$cdom,$cnum);
           if ($putresult eq 'ok') {
               if ($env{'course.'.$cid.'.num'} eq $cnum) {
                   &appenv({'course.'.$cid.'.internal.co-owners' => $coowners});
               }
           }
       }
       if ($delcoowners) {
           $delresult = &Apache::lonnet::del('environment',['internal.co-owners'],$cdom,$cnum);
           if ($delresult eq 'ok') {
               if ($env{'course.'.$cid.'.internal.co-owners'}) {
                   &Apache::lonnet::delenv('course.'.$cid.'.internal.co-owners');
               }
           }
       }
       if (($putresult eq 'ok') || ($delresult eq 'ok')) {
           my %crsinfo =
               &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,undef,undef,'.');
           if (ref($crsinfo{$cid}) eq 'HASH') {
               $crsinfo{$cid}{'co-owners'} = \@newcoowners;
               my $cidput = &Apache::lonnet::courseidput($cdom,\%crsinfo,$chome,'notime');
           }
       }
   }
   
 # -------------------------------------------------- Modify user authentication  # -------------------------------------------------- Modify user authentication
 # Overrides without validation  # Overrides without validation
   
Line 6331  sub modifyuser { Line 6810  sub modifyuser {
     my ($udom,    $uname, $uid,      my ($udom,    $uname, $uid,
         $umode,   $upass, $first,          $umode,   $upass, $first,
         $middle,  $last,  $gene,          $middle,  $last,  $gene,
         $forceid, $desiredhome, $email, $inststatus)=@_;          $forceid, $desiredhome, $email, $inststatus, $candelete)=@_;
     $udom= &LONCAPA::clean_domain($udom);      $udom= &LONCAPA::clean_domain($udom);
     $uname=&LONCAPA::clean_username($uname);      $uname=&LONCAPA::clean_username($uname);
       my $showcandelete = 'none';
       if (ref($candelete) eq 'ARRAY') {
           if (@{$candelete} > 0) {
               $showcandelete = join(', ',@{$candelete});
           }
       }
     &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.      &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.
              $umode.', '.$first.', '.$middle.', '.               $umode.', '.$first.', '.$middle.', '.
      $last.', '.$gene.'(forceid: '.$forceid.')'.               $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
              (defined($desiredhome) ? ' desiredhome = '.$desiredhome :               (defined($desiredhome) ? ' desiredhome = '.$desiredhome :
                                      ' desiredhome not specified').                                        ' desiredhome not specified'). 
              ' by '.$env{'user.name'}.' at '.$env{'user.domain'}.               ' by '.$env{'user.name'}.' at '.$env{'user.domain'}.
              ' in domain '.$env{'request.role.domain'});               ' in domain '.$env{'request.role.domain'});
     my $uhome=&homeserver($uname,$udom,'true');      my $uhome=&homeserver($uname,$udom,'true');
       my $newuser;
       if ($uhome eq 'no_host') {
           $newuser = 1;
       }
 # ----------------------------------------------------------------- Create User  # ----------------------------------------------------------------- Create User
     if (($uhome eq 'no_host') &&       if (($uhome eq 'no_host') && 
  (($umode && $upass) || ($umode eq 'localauth'))) {   (($umode && $upass) || ($umode eq 'localauth'))) {
Line 6394  sub modifyuser { Line 6883  sub modifyuser {
    ['firstname','middlename','lastname','generation','id',     ['firstname','middlename','lastname','generation','id',
                     'permanentemail','inststatus'],                      'permanentemail','inststatus'],
    $udom,$uname);     $udom,$uname);
     my %names;      my (%names,%oldnames);
     if ($tmp[0] =~ m/^error:.*/) {       if ($tmp[0] =~ m/^error:.*/) { 
         %names=();           %names=(); 
     } else {      } else {
         %names = @tmp;          %names = @tmp;
           %oldnames = %names;
     }      }
 #  #
 # Make sure to not trash student environment if instructor does not bother  # If name, email and/or uid are blank (e.g., because an uploaded file
 # to supply name and email information  # of users did not contain them), do not overwrite existing values
 #  # unless field is in $candelete array ref.  
   #
   
       my @fields = ('firstname','middlename','lastname','generation',
                     'permanentemail','id');
       my %newvalues;
       if (ref($candelete) eq 'ARRAY') {
           foreach my $field (@fields) {
               if (grep(/^\Q$field\E$/,@{$candelete})) {
                   if ($field eq 'firstname') {
                       $names{$field} = $first;
                   } elsif ($field eq 'middlename') {
                       $names{$field} = $middle;
                   } elsif ($field eq 'lastname') {
                       $names{$field} = $last;
                   } elsif ($field eq 'generation') { 
                       $names{$field} = $gene;
                   } elsif ($field eq 'permanentemail') {
                       $names{$field} = $email;
                   } elsif ($field eq 'id') {
                       $names{$field}  = $uid;
                   }
               }
           }
       }
     if ($first)  { $names{'firstname'}  = $first; }      if ($first)  { $names{'firstname'}  = $first; }
     if (defined($middle)) { $names{'middlename'} = $middle; }      if (defined($middle)) { $names{'middlename'} = $middle; }
     if ($last)   { $names{'lastname'}   = $last; }      if ($last)   { $names{'lastname'}   = $last; }
Line 6428  sub modifyuser { Line 6942  sub modifyuser {
             }              }
         }          }
     }      }
     my $reply = &put('environment', \%names, $udom,$uname);      my $logmsg = $udom.', '.$uname.', '.$uid.', '.
     if ($reply ne 'ok') { return 'error: '.$reply; }  
     my $sqlresult = &update_allusers_table($uname,$udom,\%names);  
     &devalidate_cache_new('namescache',$uname.':'.$udom);  
     my $logmsg = 'Success modifying user '.$udom.', '.$uname.', '.$uid.', '.  
                  $umode.', '.$first.', '.$middle.', '.                   $umode.', '.$first.', '.$middle.', '.
          $last.', '.$gene.', '.$email.', '.$inststatus;                   $last.', '.$gene.', '.$email.', '.$inststatus;
     if ($env{'user.name'} ne '' && $env{'user.domain'}) {      if ($env{'user.name'} ne '' && $env{'user.domain'}) {
         $logmsg .= ' by '.$env{'user.name'}.' at '.$env{'user.domain'};          $logmsg .= ' by '.$env{'user.name'}.' at '.$env{'user.domain'};
     } else {      } else {
         $logmsg .= ' during self creation';          $logmsg .= ' during self creation';
     }      }
       my $changed;
       if ($newuser) {
           $changed = 1;
       } else {
           foreach my $field (@fields) {
               if ($names{$field} ne $oldnames{$field}) {
                   $changed = 1;
                   last;
               }
           }
       }
       unless ($changed) {
           $logmsg = 'No changes in user information needed for: '.$logmsg;
           &logthis($logmsg);
           return 'ok';
       }
       my $reply = &put('environment', \%names, $udom,$uname);
       if ($reply ne 'ok') {
           return 'error: '.$reply;
       }
       if ($names{'permanentemail'} ne $oldnames{'permanentemail'}) {
           &Apache::lonnet::devalidate_cache_new('emailscache',$uname.':'.$udom);
       }
       my $sqlresult = &update_allusers_table($uname,$udom,\%names);
       &devalidate_cache_new('namescache',$uname.':'.$udom);
       $logmsg = 'Success modifying user '.$logmsg;
     &logthis($logmsg);      &logthis($logmsg);
     return 'ok';      return 'ok';
 }  }
Line 6623  sub createcourse { Line 7159  sub createcourse {
     }      }
     return $uname if ($uname =~ /^error/);      return $uname if ($uname =~ /^error/);
 # -------------------------------------------------- Check supplied server name  # -------------------------------------------------- Check supplied server name
     $course_server = $env{'user.homeserver'} if (! defined($course_server));      if (!defined($course_server)) {
     if (! &is_library($course_server)) {          if (defined(&domain($udom,'primary'))) {
         return 'error:bad server name '.$course_server;              $course_server = &domain($udom,'primary');
           } else {
               $course_server = $env{'user.home'}; 
           }
       }
       my %host_servers =
           &Apache::lonnet::get_servers($udom,'library');
       unless ($host_servers{$course_server}) {
           return 'error: invalid home server for course: '.$course_server;
     }      }
 # ------------------------------------------------------------- Make the course  # ------------------------------------------------------------- Make the course
     my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::',      my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::',
Line 6672  ENDINITMAP Line 7216  ENDINITMAP
     }      }
 # ----------------------------------------------------------- Write preferences  # ----------------------------------------------------------- Write preferences
     &writecoursepref($udom.'_'.$uname,      &writecoursepref($udom.'_'.$uname,
                      ('description' => $description,                       ('description'              => $description,
                       'url'         => $topurl));                        'url'                      => $topurl,
                         'internal.creator'         => $env{'user.name'}.':'.
                                                       $env{'user.domain'},
                         'internal.created'         => $now,
                         'internal.creationcontext' => $context)
                       );
     return '/'.$udom.'/'.$uname;      return '/'.$udom.'/'.$uname;
 }  }
   
Line 8098  sub metadata { Line 8647  sub metadata {
     }      }
  }   }
     } else {       } else { 
   
  if (defined($token->[2]->{'name'})) {    if (defined($token->[2]->{'name'})) { 
     $unikey.='_'.$token->[2]->{'name'};       $unikey.='_'.$token->[2]->{'name'}; 
  }   }
Line 9448  sub get_dns { Line 9996  sub get_dns {
     my %libserv;      my %libserv;
     my $loaded;      my $loaded;
     my %name_to_host;      my %name_to_host;
       my %internetdom;
   
     sub parse_hosts_tab {      sub parse_hosts_tab {
  my ($file) = @_;   my ($file) = @_;
Line 9455  sub get_dns { Line 10004  sub get_dns {
     next if ($configline =~ /^(\#|\s*$ )/x);      next if ($configline =~ /^(\#|\s*$ )/x);
     next if ($configline =~ /^\^/);      next if ($configline =~ /^\^/);
     chomp($configline);      chomp($configline);
     my ($id,$domain,$role,$name,$protocol)=split(/:/,$configline);      my ($id,$domain,$role,$name,$protocol,$intdom)=split(/:/,$configline);
     $name=~s/\s//g;      $name=~s/\s//g;
     if ($id && $domain && $role && $name) {      if ($id && $domain && $role && $name) {
  $hostname{$id}=$name;   $hostname{$id}=$name;
Line 9471  sub get_dns { Line 10020  sub get_dns {
                 } else {                  } else {
                     $protocol{$id} = 'http';                      $protocol{$id} = 'http';
                 }                  }
                   if (defined($intdom)) {
                       $internetdom{$id} = $intdom;
                   }
     }      }
  }   }
     }      }
Line 9532  sub get_dns { Line 10084  sub get_dns {
  return %libserv;   return %libserv;
     }      }
   
       sub unique_library {
           #2x reverse removes all hostnames that appear more than once
           my %unique = reverse &all_library();
           return reverse %unique;
       }
   
     sub get_servers {      sub get_servers {
  &load_hosts_tab() if (!$loaded);   &load_hosts_tab() if (!$loaded);
   
Line 9555  sub get_dns { Line 10113  sub get_dns {
  return %result;   return %result;
     }      }
   
       sub get_unique_servers {
           my %unique = reverse &get_servers(@_);
           return reverse %unique;
       }
   
     sub host_domain {      sub host_domain {
  &load_hosts_tab() if (!$loaded);   &load_hosts_tab() if (!$loaded);
   
Line 9569  sub get_dns { Line 10132  sub get_dns {
  my @uniq = grep(!$seen{$_}++, values(%hostdom));   my @uniq = grep(!$seen{$_}++, values(%hostdom));
  return @uniq;   return @uniq;
     }      }
   
       sub internet_dom {
           &load_hosts_tab() if (!$loaded);
   
           my ($lonid) = @_;
           return $internetdom{$lonid};
       }
 }  }
   
 {   { 
Line 9686  sub get_dns { Line 10256  sub get_dns {
         return undef;          return undef;
     }      }
   
       sub get_internet_names {
           my ($lonid) = @_;
           return if ($lonid eq '');
           my ($idnref,$cached)=
               &Apache::lonnet::is_cached_new('internetnames',$lonid);
           if ($cached) {
               return $idnref;
           }
           my $ip = &get_host_ip($lonid);
           my @hosts = &get_hosts_from_ip($ip);
           my %iphost = &get_iphost();
           my (@idns,%seen);
           foreach my $id (@hosts) {
               my $dom = &host_domain($id);
               my $prim_id = &domain($dom,'primary');
               my $prim_ip = &get_host_ip($prim_id);
               next if ($seen{$prim_ip});
               if (ref($iphost{$prim_ip}) eq 'ARRAY') {
                   foreach my $id (@{$iphost{$prim_ip}}) {
                       my $intdom = &internet_dom($id);
                       unless (grep(/^\Q$intdom\E$/,@idns)) {
                           push(@idns,$intdom);
                       }
                   }
               }
               $seen{$prim_ip} = 1;
           }
           return &Apache::lonnet::do_cache_new('internetnames',$lonid,\@idns,12*60*60);
       }
   
   }
   
   sub all_loncaparevs {
       return qw(1.1 1.2 1.3 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10);
 }  }
   
 BEGIN {  BEGIN {
Line 9763  BEGIN { Line 10367  BEGIN {
     close($config);      close($config);
 }  }
   
   # ---------------------------------------------------------- Read loncaparev table
   {
       if (-e "$perlvar{'lonTabDir'}/loncaparevs.tab") {
           if (open(my $config,"<$perlvar{'lonTabDir'}/loncaparevs.tab")) {
               while (my $configline=<$config>) {
                   chomp($configline);
                   my ($hostid,$loncaparev)=split(/:/,$configline);
                   $loncaparevs{$hostid}=$loncaparev;
               }
               close($config);
           }
       }
   }
   
   # ---------------------------------------------------------- Read serverhostID table
   {
       if (-e "$perlvar{'lonTabDir'}/serverhomeIDs.tab") {
           if (open(my $config,"<$perlvar{'lonTabDir'}/serverhomeIDs.tab")) {
               while (my $configline=<$config>) {
                   chomp($configline);
                   my ($name,$id)=split(/:/,$configline);
                   $serverhomeIDs{$name}=$id;
               }
               close($config);
           }
       }
   }
   
   {
       my $file = $Apache::lonnet::perlvar{'lonTabDir'}.'/releaseslist.xml';
       if (-e $file) {
           my $parser = HTML::LCParser->new($file);
           while (my $token = $parser->get_token()) {
               if ($token->[0] eq 'S') {
                   my $item = $token->[1];
                   my $name = $token->[2]{'name'};
                   my $value = $token->[2]{'value'};
                   if ($item ne '' && $name ne '' && $value ne '') {
                       my $release = $parser->get_text();
                       $release =~ s/(^\s*|\s*$ )//gx;
                       $needsrelease{$item.':'.$name.':'.$value} = $release;
                   }
               }
           }
       }
   }
   
 # ------------- set up temporary directory  # ------------- set up temporary directory
 {  {
     $tmpdir = $perlvar{'lonDaemons'}.'/tmp/';      $tmpdir = $perlvar{'lonDaemons'}.'/tmp/';
Line 9993  authentication scheme Line 10644  authentication scheme
   
 =item *  =item *
 X<authenticate()>  X<authenticate()>
 B<authenticate($uname,$upass,$udom)>: try to  B<authenticate($uname,$upass,$udom,$checkdefauth,$clientcancheckhost)>: try to
 authenticate user from domain's lib servers (first use the current  authenticate user from domain's lib servers (first use the current
 one). C<$upass> should be the users password.  one). C<$upass> should be the users password.
   $checkdefauth is optional (value is 1 if a check should be made to
      authenticate user using default authentication method, and allow
      account creation if username does not have account in the domain).
   $clientcancheckhost is optional (value is 1 if checking whether the
      server can host will occur on the client side in lonauth.pm).
   
 =item *  =item *
 X<homeserver()>  X<homeserver()>
Line 10117  modifyuserauth($udom,$uname,$umode,$upas Line 10773  modifyuserauth($udom,$uname,$umode,$upas
   
 =item *  =item *
   
 modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,  modifyuser($udom,$uname,$uid,$umode,$upass,$first,$middle,$last, $gene,
            $forceid,$desiredhome,$email,$inststatus) :              $forceid,$desiredhome,$email,$inststatus,$candelete) :
 modify user  
   will update user information (firstname,middlename,lastname,generation,
   permanentemail), and if forceid is true, student/employee ID also.
   A user's institutional affiliation(s) can also be updated.
   User information fields will not be overwritten with empty entries 
   unless the field is included in the $candelete array reference.
   This array is included when a single user is modified via "Manage Users",
   or when Autoupdate.pl is run by cron in a domain.
   
 =item *  =item *
   

Removed from v.1.1042  
changed lines
  Added in v.1.1056.4.11


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>