Diff for /loncom/interface/lonmsg.pm between versions 1.18 and 1.248

version 1.18, 2001/08/09 15:29:37 version 1.248, 2022/01/18 17:33:13
Line 1 Line 1
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 #  
 # Routines for messaging  # Routines for messaging
 #  #
 # (Routines to control the menu  # $Id$
   #
   # 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.
 #  #
 # (TeX Conversion Module  # 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.
 #  #
 # 05/29/00,05/30 Gerd Kortemeyer)  # 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
 #  #
 # 10/05 Gerd Kortemeyer)  # /home/httpd/html/adm/gpl.txt
   #
   # http://www.lon-capa.org/
 #  #
 # 10/19,10/20,10/30,  
 # 02/06/01 Gerd Kortemeyer  
 # 07/27 Guy Albertelli  
 # 07/27,07/28,07/30,08/03,08/06,08/08,08/09 Gerd Kortemeyer  
   
 package Apache::lonmsg;  package Apache::lonmsg;
   
   =pod
   
   =head1 NAME
   
   Apache::lonmsg: supports internal messaging
   
   =head1 SYNOPSIS
   
   lonmsg provides routines for sending messages.
   
   Right now, this document will cover just how to send a message, since
   it is likely you will not need to programmatically read messages,
   since lonmsg already implements that functionality.
   
   The routines used to package messages and unpackage messages are not
   only used by lonmsg when creating/extracting messages for LON-CAPA's
   internal messaging system, but also by lonnotify.pm which is available
   for use by Domain Coordinators to broadcast standard e-mail to specified
   users in their domain.  The XML packaging used in the two cases is very
   similar.  The differences are the use of <recuser>$uname</recuser> and
   <recdomain>$udom</recdomain> in stored internal messages, compared
   with <recipient username="$uname:$udom">$email</recipient> in stored
   Domain Coordinator e-mail for the storage of information about
   recipients of the message/e-mail.
   
   =head1 SUBROUTINES
   
   =over
   
   =pod
   
   =item packagemsg()
   
   Package
   
   =item get_course_context()
   
   =item unpackagemsg()
   
   Unpack message into a hash
   
   =item buildmsgid()
   
   Get info out of msgid
   
   =item unpackmsgid()
   
   =item sendemail()
   
   =item sendnotification()
   
   Send notification emails
   
   =item newmail()
   
   Check for email
   
   =item author_res_msg()
   
   Automated message to the author of a resource
   
   =item * B<author_res_msg($filename, $message)>: Sends message $message to the owner
       of the resource with the URI $filename.
   
   =item retrieve_author_res_msg()
   
   Retrieve author resource messages
   
   =item del_url_author_res_msg()
   
   Delete all author messages related to one URL
   
   =item clear_author_res_msg()
   
   Clear out all author messages in URL path
   
   =item all_url_author_res_msg()
   
   Return hash with URLs for which there is a resource message
   
   =item store_instructor_comment()
   
   Add a comment to the User Notes screen
   
   =item user_crit_msg_raw()
   
   Critical message to a user
   
   =item user_crit_msg()
   
   New routine that respects "forward" and calls old routine
   
   =item * B<user_crit_msg($user, $domain, $subject, $message, $sendback, $toperm, $sentmessage, $nosentstore, $recipid, $attachmenturl, $permresults, $senthide)>: 
       Sends a critical message $message to the $user at $domain.  If $sendback
       is true,  a receipt will be sent to the current user when $user receives 
       the message.
   
       Additionally it will check if the user has a Forwarding address
       set, and send the message to that address instead
   
       returns 
         - in array context a list of results for each message that was sent
         - in scalar context a space seperated list of results for each 
              message sent
   
   
   =item user_crit_received()
   
   Critical message received
   
   =item user_normal_msg_raw()
   
   Normal communication
   
   =item user_normal_msg()
   
   New routine that respects "forward" and calls old routine
   
   =item * B<user_normal_msg($user, $domain, $subject, $message, $citation,
          $baseurl, $attachmenturl, $toperm, $sentmessage, $symb, $restitle,
          $error,$nosentstore,$recipid,$permresults,$senthide)>:
    Sends a message to the  $user at $domain, with subject $subject and message $message.
   
       Additionally it will check if the user has a Forwarding address
       set, and send the message to that address instead
   
       returns
         - in array context a list of results for each message that was sent
         - in scalar context a space seperated list of results for each
              message sent
   
   =item store_sent_mail()
   
   =item store_recipients()
   
   =item foldersuffix()
   
   =item get_user_folders()
   
   User-defined folders 
   
   =item secapply()
   
   =item B<decide_receiver($feedurl,$author,$question,$course,$policy,$defaultflag)>:
   
   Arguments
     $feedurl - /res/ url of resource (only need if $author is true)
     $author,$question,$course,$policy - all true/false parameters
       if true will attempt to find the addresses of user that should receive
       this type of feedback (author - feedback to author of resource $feedurl,
       $question 'Resource Content Questions', $course 'Course Content Question',
       $policy 'Course Policy')
       (Additionally it also checks $env for whether the corresponding form.<name>
       element exists, for ease of use in a html response context)
      
     $defaultflag - (internal should be left blank) if true gather addresses 
                    that aren't for a section even if I have a section
                    (used for reccursion internally, first we look for
                    addresses for our specific section then we recurse
                    and look for non section addresses)
   
   Returns
     $typestyle - string of html text, describing what addresses were found
     %to - a hash, which keys are addresses of users to send messages to
           the keys will look like   name:domain
   
   =back
   
   =cut
   
 use strict;  use strict;
 use Apache::lonnet();  use Apache::lonnet;
 use vars qw($msgcount);  use Apache::loncommon;
 use HTML::TokeParser;  use HTML::TokeParser();
 use Apache::Constants qw(:common);  use Apache::lonlocal;
   use HTML::Entities;
   use Encode;
   use LONCAPA qw(:DEFAULT :match);
   
   {
       my $uniq;
       sub get_uniq {
    $uniq++;
    return $uniq;
       }
   }
   
   
 # ===================================================================== Package  
   
 sub packagemsg {  sub packagemsg {
     my ($subject,$message,$citation)=@_;      my ($subject,$message,$citation,$baseurl,$attachmenturl,$recuser,$recdomain,
     $message=~s/\</\&lt\;/g;   $msgid,$type,$crsmsgid,$symb,$error,$recipid,$senthide,$origmsgid)=@_;
     $message=~s/\>/\&gt\;/g;      $message =&HTML::Entities::encode($message,'<>&"');
     $citation=~s/\</\&lt\;/g;      $citation=&HTML::Entities::encode($citation,'<>&"');
     $citation=~s/\>/\&gt\;/g;      $subject =&HTML::Entities::encode($subject,'<>&"');
     $subject=~s/\</\&lt\;/g;      #remove machine specification
     $subject=~s/\>/\&gt\;/g;      $baseurl =~ s|^https?://[^/]+/|/|;
       $baseurl =&HTML::Entities::encode($baseurl,'<>&"');
       #remove machine specification
       $attachmenturl =~ s|^https?://[^/]+/|/|;
       $attachmenturl =&HTML::Entities::encode($attachmenturl,'<>&"');
       if ($senthide) {
           foreach my $item ($subject,$message) {
               if ($item ne '') {
                   $item = 'Not shown due to IP block';
               }
           }
           if ($attachmenturl ne '') {
               $attachmenturl = '';
           }
           if ($citation ne '') {
               $citation = '';
           }
           if ($msgid ne '') {
               $msgid = '';
           }
       }
       my $course_context = &get_course_context();
     my $now=time;      my $now=time;
     $msgcount++;      my $ip = &Apache::lonnet::get_requestor_ip();
     my $partsubj=$subject;      my $msgcount = &get_uniq();
     $partsubj=&Apache::lonnet::escape($partsubj);      unless(defined($msgid)) {
     $partsubj=substr($partsubj,0,50);          $msgid = &buildmsgid($now,$subject,$env{'user.name'},$env{'user.domain'},
     my $msgid=&Apache::lonnet::escape(                             $msgcount,$course_context,$symb,$error,$$);
            $now.':'.$partsubj.':'.$ENV{'user.name'}.':'.      }
            $ENV{'user.domain'}.':'.$msgcount.':'.$$);      my $result = '<sendername>'.$env{'user.name'}.'</sendername>'.
     return $msgid,             '<senderdomain>'.$env{'user.domain'}.'</senderdomain>'.
            '<sendername>'.$ENV{'user.name'}.'</sendername>'.  
            '<senderdomain>'.$ENV{'user.domain'}.'</senderdomain>'.  
            '<subject>'.$subject.'</subject>'.             '<subject>'.$subject.'</subject>'.
    '<time>'.localtime($now).'</time>'.             '<time>'.&Apache::lonlocal::locallocaltime($now).'</time>';
    '<servername>'.$ENV{'SERVER_NAME'}.'</servername>'.      if (defined($crsmsgid)) {
           $result.= '<courseid>'.$course_context.'</courseid>'.
                     '<coursesec>'.$env{'request.course.sec'}.'</coursesec>'.
                     '<msgid>'.$msgid.'</msgid>'.
                     '<coursemsgid>'.$crsmsgid.'</coursemsgid>'.
                     '<message>'.$message.'</message>';
           return ($msgid,$result);
       }
       $result .= '<servername>'.$ENV{'SERVER_NAME'}.'</servername>'.
            '<host>'.$ENV{'HTTP_HOST'}.'</host>'.             '<host>'.$ENV{'HTTP_HOST'}.'</host>'.
    '<client>'.$ENV{'REMOTE_ADDR'}.'</client>'.     '<client>'.$ip.'</client>'.
    '<browsertype>'.$ENV{'browser.type'}.'</browsertype>'.     '<browsertype>'.$env{'browser.type'}.'</browsertype>'.
    '<browseros>'.$ENV{'browser.os'}.'</browseros>'.     '<browseros>'.$env{'browser.os'}.'</browseros>'.
    '<browserversion>'.$ENV{'browser.version'}.'</browserversion>'.     '<browserversion>'.$env{'browser.version'}.'</browserversion>'.
            '<browsermathml>'.$ENV{'browser.mathml'}.'</browsermathml>'.             '<browsermathml>'.$env{'browser.mathml'}.'</browsermathml>'.
    '<browserraw>'.$ENV{'HTTP_USER_AGENT'}.'</browserraw>'.     '<browserraw>'.$ENV{'HTTP_USER_AGENT'}.'</browserraw>'.
    '<courseid>'.$ENV{'request.course.id'}.'</courseid>'.     '<courseid>'.$course_context.'</courseid>'.
    '<role>'.$ENV{'request.role'}.'</role>'.     '<coursesec>'.$env{'request.course.sec'}.'</coursesec>'.
    '<resource>'.$ENV{'request.filename'}.'</resource>'.     '<role>'.$env{'request.role'}.'</role>'.
            '<msgid>'.$msgid.'</msgid>'.     '<resource>'.$env{'request.filename'}.'</resource>'.
    '<message>'.$message.'</message>'.             '<msgid>'.$msgid.'</msgid>';
    '<citation>'.$citation.'</citation>';      if (defined($env{'form.group'})) {
           $result .= '<group>'.$env{'form.group'}.'</group>';
       }
       if (ref($recuser) eq 'ARRAY') {
           for (my $i=0; $i<@{$recuser}; $i++) {
               if ($type eq 'dcmail') {
                   my ($username,$email) = split(/:/,$$recuser[$i]);
                   $username = &unescape($username);
                   $email = &unescape($email);
                   $username = &HTML::Entities::encode($username,'<>&"');
                   $email = &HTML::Entities::encode($email,'<>&"');
                   $result .= '<recipient username="'.$username.'">'.
                                               $email.'</recipient>';
               } else {
                   $result .= '<recuser>'.$$recuser[$i].'</recuser>'.
                              '<recdomain>'.$$recdomain[$i].'</recdomain>';
               }
           }
       } else {
           $result .= '<recuser>'.$recuser.'</recuser>'.
                      '<recdomain>'.$recdomain.'</recdomain>';
       }
       $result .= '<message>'.$message.'</message>';
       if (defined($citation)) {
    $result.='<citation>'.$citation.'</citation>';
       }
       if (defined($baseurl)) {
    $result.= '<baseurl>'.$baseurl.'</baseurl>';
       }
       if (defined($attachmenturl)) {
    $result.= '<attachmenturl>'.$attachmenturl.'</attachmenturl>';
       }
       if (defined($symb)) {
           $result.= '<symb>'.$symb.'</symb>';
           if ($course_context ne '') {
               if ($course_context eq $env{'request.course.id'}) {
                   my $resource_title = &Apache::lonnet::gettitle($symb);
                   if (defined($resource_title)) {
                       $result .= '<resource_title>'.$resource_title.'</resource_title>';
                   }
               }
           }
       }
       if (defined($recipid)) {
           $result.= '<recipid>'.$recipid.'</recipid>';
       }
       if ($env{'form.can_reply'} eq 'N') {
           $result .= '<noreplies>1</noreplies>';
       }
       if ($env{'form.reply_to_addr'}) {
           my ($replytoname,$replytodom) = split(/:/,$env{'form.reply_to_addr'});
           if (!($replytoname eq $env{'user.name'} && $replytodom eq $env{'user.domain'})) {
               if (&Apache::lonnet::homeserver($replytoname,$replytodom) ne 'no_host') {
                   $result .= '<replytoaddr>'.$env{'form.reply_to_addr'}.'</replytoaddr>';
               }
           }
       }
       if ($senthide) {
           $result .= '<senthide>$origmsgid</senthide>';
       }
       return ($msgid,$result);
   }
   
   sub get_course_context {
       my $course_context;
       my $msgkey;
       if (defined($env{'form.replyid'})) {
           $msgkey = $env{'form.replyid'};
       } elsif (defined($env{'form.forwid'})) {
           $msgkey = $env{'form.forwid'}
       } elsif (defined($env{'form.multiforwid'})) {
           $msgkey = $env{'form.multiforwid'};
       }
       if ($msgkey ne '') {
           my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid)=
                      split(/\:/,&unescape($msgkey));
           $course_context = $origcid;
       }
       foreach my $key (keys(%env)) {
           if ($key=~/^form\.(rep)?rec\_(.*)$/) {
               my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid) =
                                       split(/\:/,&unescape($2));
               $course_context = $origcid;
               last;
           }
       }
       if ($course_context eq '') {
           $course_context = $env{'request.course.id'};
       }
       return $course_context;
 }  }
   
 # ================================================== Unpack message into a hash  
   
 sub unpackagemsg {  sub unpackagemsg {
     my $message=shift;      my ($message,$notoken,$noattachmentlink)=@_;
     my %content=();      my %content=();
     my $parser=HTML::TokeParser->new(\$message);      my $parser=HTML::TokeParser->new(\$message);
     my $token;      my $token;
Line 73  sub unpackagemsg { Line 380  sub unpackagemsg {
        if ($token->[0] eq 'S') {         if ($token->[0] eq 'S') {
    my $entry=$token->[1];     my $entry=$token->[1];
            my $value=$parser->get_text('/'.$entry);             my $value=$parser->get_text('/'.$entry);
            $content{$entry}=$value;             if (($entry eq 'recuser') || ($entry eq 'recdomain')) {
                  push(@{$content{$entry}},$value);
              } elsif ($entry eq 'recipient') {
                  my $username = $token->[2]{'username'};
                  $username = &HTML::Entities::decode($username,'<>&"');
                  $content{$entry}{$username} = $value;
              } else {
                  $content{$entry}=$value;
              }
          }
       }
       if (!exists($content{'recuser'})) { $content{'recuser'} = []; }
       if (($content{'attachmenturl'}) && (!$noattachmentlink)) {
          my ($fname)=($content{'attachmenturl'}=~m|/([^/]+)$|);
          if ($notoken) {
      $content{'message'}.='<p>'.&mt('Attachment').': <tt>'.$fname.'</tt>';
          } else {
      &Apache::lonnet::allowuploaded('/adm/msg',
     $content{'attachmenturl'});
      $content{'message'}.='<p>'.&mt('Attachment').
          ': <a href="'.$content{'attachmenturl'}.'"><tt>'.
          $fname.'</tt></a>';
        }         }
     }      }
     return %content;      return %content;
 }  }
   
 # ======================================================= Get info out of msgid  
   sub buildmsgid {
       my ($now,$subject,$uname,$udom,$msgcount,$course_context,$symb,$error,$pid) = @_;
       $subject=&escape($subject);
       $symb = &escape($symb);
       return(&escape($now.':'.$subject.':'.$uname.':'.
              $udom.':'.$msgcount.':'.$course_context.':'.$pid.':'.$symb.':'.$error));
   }
   
 sub unpackmsgid {  sub unpackmsgid {
     my $msgid=&Apache::lonnet::unescape(shift);      my ($msgid,$folder,$skipstatus,$status_cache,$onlycid)=@_;
     my ($sendtime,$shortsubj,$fromname,$fromdomain)=split(/\:/,      $msgid=&unescape($msgid);
                           &Apache::lonnet::unescape($msgid));      my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$fromcid,
     my %status=&Apache::lonnet::get('email_status',[$msgid]);          $processid,$symb,$error) = split(/\:/,&unescape($msgid));
     if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; }      if (!defined($processid)) { $fromcid = ''; }
     unless ($status{$msgid}) { $status{$msgid}='new'; }      if (($onlycid) && ($onlycid ne $fromcid)) {
     return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid});          return ($sendtime,'',$fromname,$fromdomain,'',$fromcid,'',$error);
 }       }
       $shortsubj = &unescape($shortsubj);
       $shortsubj = &HTML::Entities::decode($shortsubj);
       $symb = &unescape($symb);
       my %status=();
       unless ($skipstatus) {
    if (ref($status_cache)) {
       $status{$msgid} = $status_cache->{$msgid};
    } else {
       my $suffix=&foldersuffix($folder);
       %status=&Apache::lonnet::get('email_status'.$suffix,[$msgid]);
    }
    if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; }
           unless ($status{$msgid}) { $status{$msgid}='new'; }
       }
       return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid},$fromcid,$symb,$error);
   }
   
   
   sub sendemail {
       my ($to,$subject,$body,$to_uname,$to_udom,$user_lh,$attachmenturl)=@_;
       my $senderaddress='';
       my $replytoaddress='';
       my $msgsent;
       if ($env{'form.can_reply'} eq 'N') {
           my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
           my $hostname = &Apache::lonnet::hostname($lonhost);
           $replytoaddress = 'do-not-reply@'.$hostname;
       } else {
           my %senderemails;
           my $have_sender;
           if ($env{'form.reply_to_addr'}) {
               my ($replytoname,$replytodom) = split(/:/,$env{'form.reply_to_addr'});
               if (!($replytoname eq $env{'user.name'} && $replytodom eq $env{'user.domain'})) {
                   if (&Apache::lonnet::homeserver($replytoname,$replytodom) ne 'no_host') {
                       %senderemails = 
                           &Apache::loncommon::getemails($replytoname,$replytodom);
                       $have_sender = 1;
                   }
               }
           }
           if (!$have_sender) {
               %senderemails=&Apache::loncommon::getemails();
           }
           foreach my $type ('permanentemail','critnotification','notification') {
               if ($senderemails{$type}) {
                   ($senderaddress) = split(/,/,$senderemails{$type});
                   last if ($senderaddress);
               }
           }
       }
       $body=
       "*** ".&mt_user($user_lh,'This is an automatic e-mail generated by the LON-CAPA system.')."\n".
       "*** ".($senderaddress?&mt_user($user_lh,'You can reply to this e-mail'):&mt_user($user_lh,'Please do not reply to this address.')."\n*** ".
       &mt_user($user_lh,'A reply will not be received by the recipient!'))."\n\n".$body;
       
       $attachmenturl = &Apache::lonnet::filelocation("",$attachmenturl);
       my $filesize = (stat($attachmenturl))[7];
       if ($filesize > 1048576) {
           # Don't send if it exceeds 1 MB.
           print '<p><span class="LC_error">' 
               .&mt('Email not sent.  Attachment exceeds permitted length.')
               .'</span><br /></p>';
       } else {
           # Otherwise build and send the email
           $subject = '[LON-CAPA] '.$subject;
           &Apache::loncommon::mime_email($senderaddress,$replytoaddress,$to,
                                          $subject,$body,'','',$attachmenturl,'','');
           $msgsent = 1;
       }
       return $msgsent;
   }
   
   # ==================================================== Send notification emails
   
   sub sendnotification {
       my ($to,$touname,$toudom,$subj,$crit,$text,$msgid,$attachmenturl)=@_;
       my $sender=$env{'environment.firstname'}.' '.$env{'environment.lastname'};
       unless ($sender=~/\w/) { 
    $sender=$env{'user.name'}.':'.$env{'user.domain'};
       }
       my $critical=($crit?' critical':'');
       my $numsent = 0;
   
       $text=~s/\&lt\;/\</gs;
       $text=~s/\&gt\;/\>/gs;
       my $homeserver = &Apache::lonnet::homeserver($touname,$toudom);
       my $hostname = &Apache::lonnet::hostname($homeserver);
       my $protocol = $Apache::lonnet::protocol{$homeserver};
       $protocol = 'http' if ($protocol ne 'https');
   #FIXME
       my $url = $protocol.'://'.$hostname.
                 '/adm/email?username='.$touname.'&domain='.$toudom.
                 '&display='.&escape($msgid);
       my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid,
           $symb,$error) = &Apache::lonmsg::unpackmsgid($msgid);
       my ($coursetext,$body,$bodybegin,$bodysubj,$bodyend);
       my $user_lh = &Apache::loncommon::user_lang($touname,$toudom,$fromcid);
       if ($fromcid ne '') {
           $coursetext = "\n".&mt_user($user_lh,'Course').': ';
           if ($env{'course.'.$fromcid.'.description'} ne '') {
               $coursetext .= $env{'course.'.$fromcid.'.description'};
           } else {
               my %coursehash = &Apache::lonnet::coursedescription($fromcid,);
               if ($coursehash{'description'} ne '') {
                   $coursetext .= $coursehash{'description'};
               }
           }
           $coursetext .= "\n\n";
       }
       my @recipients = split(/,/,$to);
       $bodybegin = $coursetext. 
                  &mt_user($user_lh,
                            'You received a'.$critical.' message from [_1] in LON-CAPA.',$sender).' ';
       $bodysubj = &mt_user($user_lh,'The subject is 
   
    [_1]
   
   ',$subj)."\n".
   '=== '.&mt_user($user_lh,'Excerpt')." ============================================================
   ";
       $bodyend = "
   ========================================================================
   
   ".&mt_user($user_lh,'Use 
   
    [_1]
   
   to access the full message.',$url);
       my %userenv = &Apache::lonnet::get('environment',['notifywithhtml'],$toudom,$touname);
       my $subject = &mt_user($user_lh,"'New'$critical message from [_1]",$sender);
       unless ($subj eq '') {
           $subject = $subj;
       }
    
       my ($blocked,$blocktext,$clientip);
       $clientip = &Apache::lonnet::get_requestor_ip();
       if (!$crit) {
           my %setters;
           my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = 
               &Apache::loncommon::blockcheck(\%setters,'com',$clientip,$touname,$toudom);
           if ($startblock && $endblock) {
               $blocked = 1;
               my $showstart = &Apache::lonlocal::locallocaltime($startblock);
               my $showend = &Apache::lonlocal::locallocaltime($endblock);
               $blocktext = &mt_user($user_lh,'LON-CAPA messages sent to you between [_1] and [_2] will be inaccessible until the end of this time period, because you are a student in a course with an active communications block.',$showstart,$showend);
           } elsif ($by_ip) {
               $blocked = 1;
               $blocktext = &mt_user($user_lh,'LON-CAPA messages sent to you will be inaccessible from your IP address [_1], because communication is being blocked for certain IP address(es).',$clientip);
           }
       }
       if ($userenv{'notifywithhtml'} ne '') {
           my @htmlexcerpt = split(/,/,$userenv{'notifywithhtml'});
           my $htmlfree = &make_htmlfree($text);
           foreach my $addr (@recipients) {
               if ($blocked) {
                   $body = $bodybegin."\n".$blocktext."\n".$bodyend;
               } else {
                   my $sendtext;
                   if (!grep/^\Q$addr\E/,@htmlexcerpt) {
                       $sendtext = $htmlfree;
                   } else {
                       $sendtext = $text;
                   }
                   $body = $bodybegin.$bodysubj.$sendtext.$bodyend;
               }
               if (&sendemail($addr,$subject,$body,$touname,$toudom,$user_lh,$attachmenturl)) {
                   $numsent ++;
               }
           }
       } else {
           if ($blocked) {
               $body = $bodybegin."\n".$blocktext."\n".$bodyend;
           } else {
               my $htmlfree = &make_htmlfree($text);
               $body = $bodybegin.$bodysubj.$htmlfree.$bodyend;
           }
           if (&sendemail($to,$subject,$body,$touname,$toudom,$user_lh,$attachmenturl)) {
               $numsent ++;
           }
       }
       return $numsent;
   }
   
   sub make_htmlfree {
       my ($text) = @_;
       $text =~ s/\<\/*[^\>]+\>//gs;
       $text = &HTML::Entities::decode($text);
       $text = &Encode::encode('utf8',$text);
       return $text;
   }
   
   sub mynewmail{
    &newmail();
    return $env{'user.mailcheck.lastnewmessagetime'} > $env{'user.mailcheck.lastvisit'};
   }
   
   
   sub newmail {
       if ((time-$env{'user.mailcheck.time'})>300) {
           my %what=&Apache::lonnet::get('email_status',['recnewemail']);
           &Apache::lonnet::appenv({'user.mailcheck.time'=>time});
    &Apache::lonnet::appenv({'user.mailcheck.lastnewmessagetime'=> $what{'recnewemail'}});
           if ($what{'recnewemail'}>0) { return 1; }
       }
       return 0;
   }
   
   
 # =============================== Automated message to the author of a resource  
   
 sub author_res_msg {  sub author_res_msg {
     my ($filename,$message)=@_;      my ($filename,$message)=@_;
Line 101  sub author_res_msg { Line 642  sub author_res_msg {
     my $homeserver=&Apache::lonnet::homeserver($author,$domain);      my $homeserver=&Apache::lonnet::homeserver($author,$domain);
     if ($homeserver ne 'no_host') {      if ($homeserver ne 'no_host') {
        my $id=unpack("%32C*",$message);         my $id=unpack("%32C*",$message);
          $message .= " <p>This error occurred on machine ".
      $Apache::lonnet::perlvar{'lonHostID'}."</p>";
        my $msgid;         my $msgid;
        ($msgid,$message)=&packagemsg($filename,$message);         ($msgid,$message)=&packagemsg($filename,$message);
        return &Apache::lonnet::reply('put:'.$domain.':'.$author.         return &Apache::lonnet::reply('put:'.$domain.':'.$author.
          ':nohist_res_msgs:'.           ':nohist_res_msgs:'.
           &Apache::lonnet::escape($filename.'_'.$id).'='.            &escape($filename.'_'.$id).'='.
           &Apache::lonnet::escape($message),$homeserver);            &escape($message),$homeserver);
     }      }
     return 'no_host';      return 'no_host';
 }  }
   
 # ================================================== Critical message to a user  
   
 sub user_crit_msg {  
     my ($user,$domain,$subject,$message)=@_;  sub retrieve_author_res_msg {
       my $url=shift;
       $url=&Apache::lonnet::declutter($url);
       my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//);
       my %errormsgs=&Apache::lonnet::dump('nohist_res_msgs',$domain,$author);
       my $msgs='';
       foreach my $msg (keys(%errormsgs)) {
    if ($msg =~ /^\Q$url\E\_\d+$/) {
       my %content=&unpackagemsg($errormsgs{$msg});
       $msgs.='<p><img src="/adm/lonMisc/bomb.gif" /><b>'.
    $content{'time'}.'</b>: '.$content{'message'}.
    '<br /></p>';
    }
       } 
       return $msgs;     
   }
   
   
   
   
   
   sub del_url_author_res_msg {
       my $url=shift;
       $url=&Apache::lonnet::declutter($url);
       my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//);
       my @delmsgs=();
       foreach my $msg (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {
    if ($msg =~ /^\Q$url\E\_\d+$/) {
       push (@delmsgs,$msg);
    }
       }
       return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author);
   }
   
   
   sub clear_author_res_msg {
       my $url=shift;
       $url=&Apache::lonnet::declutter($url);
       my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//);
       my @delmsgs=();
       foreach my $msg (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {
    if ($msg =~ /^\Q$url\E/) {
       push (@delmsgs,$msg);
    }
       }
       return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author);
   }
   
   
   
   sub all_url_author_res_msg {
       my ($author,$domain)=@_;
       my %returnhash=();
       foreach my $msg (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {
    $msg =~ /^(.+)\_\d+/;
    $returnhash{$1}=1;
       }
       return %returnhash;
   }
   
   
   sub store_instructor_comment {
       my ($msg,$uname,$udom) = @_;
       my $cid  = $env{'request.course.id'};
       my $cnum = $env{'course.'.$cid.'.num'};
       my $cdom = $env{'course.'.$cid.'.domain'};
       my $subject= &mt('Record').' ['.$uname.':'.$udom.']';
       my $result = &user_normal_msg_raw($cnum,$cdom,$subject,$msg);
       if ($result eq 'ok' || $result eq 'con_delayed') {
           
       }
       return $result;
   }
   
   
   sub user_crit_msg_raw {
       my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage,
           $nosentstore,$recipid,$attachmenturl,$permresults,$senthide)=@_;
 # Check if allowed missing  # Check if allowed missing
     my $status='';      my ($status,$packed_message);
     my $msgid='undefined';      my $msgid='undefined';
     unless (($message)&&($user)&&($domain)) { $status='empty'; };      unless (($message)&&($user)&&($domain)) { $status='empty'; };
       my $text=$message;
     my $homeserver=&Apache::lonnet::homeserver($user,$domain);      my $homeserver=&Apache::lonnet::homeserver($user,$domain);
     if ($homeserver ne 'no_host') {      if ($homeserver ne 'no_host') {
        my $msgid;         ($msgid,$packed_message)=&packagemsg($subject,$message,undef,undef,
        ($msgid,$message)=&packagemsg($subject,$message);                                    $attachmenturl,undef,undef,undef,undef,undef,
        $status=&Apache::lonnet::critical(                                    undef,undef,$recipid);
            'put:'.$domain.':'.$user.':critical:'.         if ($sendback) { $packed_message.='<sendback>true</sendback>'; }
            &Apache::lonnet::escape($msgid).'='.         $status=&Apache::lonnet::cput('critical', {$msgid => $packed_message},
            &Apache::lonnet::escape($message),$homeserver);       $domain,$user);
           if (defined($sentmessage)) {
               $$sentmessage = $packed_message;
           }
           if (!$nosentstore) {
               my ($sentmsgid,$packed_message_no_citation) =
               &packagemsg($subject,$message,undef,undef,$attachmenturl,$user,
                           $domain,$msgid,undef,undef,undef,undef,undef,$senthide,$msgid);
               if ($status eq 'ok' || $status eq 'con_delayed') {
                   if ($senthide && $sentmsgid) {
                       &store_sent_mail($sentmsgid,$packed_message_no_citation);
                   } else {
                       &store_sent_mail($msgid,$packed_message_no_citation);
                   }
               }
           }
     } else {      } else {
        $status='no_host';         $status='no_host';
     }      }
   
   # Notifications
       my %userenv = &Apache::loncommon::getemails($user,$domain);
       my $critnotify = $userenv{'critnotification'};
       my $permemail = $userenv{'permanentemail'};
       my $numcrit = 0;
       my $numperm = 0;
       my $permlogmsgstatus;
       if ($critnotify) {
           $numcrit = &sendnotification($critnotify,$user,$domain,$subject,1,$text,$msgid,$attachmenturl);
       }
       if ($toperm && $permemail) {
           if ($critnotify && $numcrit) {
               if (grep(/^\Q$permemail\E/,split(/,/,$critnotify))) {
                   $numperm = 1;
               }
           }
           unless ($numperm) {
               $numperm = &sendnotification($permemail,$user,$domain,$subject,1,$text,$msgid,$attachmenturl);
           }
       }
       if ($toperm) {
           $permlogmsgstatus = '. Perm. email log status '.
                               &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
                                                    "Perm. e-mail count $numperm for $user at $domain");
           if (ref($permresults) eq 'HASH') {
               $permresults->{"$user:$domain"} = $numperm;
           }
       }
   # Log this
     &Apache::lonnet::logthis(      &Apache::lonnet::logthis(
       'Sending critical email '.$msgid.        'Sending critical '.$msgid.
       ', log status: '.        ', log status: '.
       &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},        &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
                          $ENV{'user.home'},                           $env{'user.home'},
       'Sending critical '.$msgid.' to '.$user.' at '.$domain.' with status: '        'Sending critical '.$msgid.' to '.$user.' at '.$domain.' with status '
       .$status));        .$status).$permlogmsgstatus);
     return $status;      return $status;
 }  }
   
 # =================================================== Critical message received  
   
   sub user_crit_msg {
       my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage,
           $nosentstore,$recipid,$attachmenturl,$permresults,$senthide)=@_;
       my @status;
       my %userenv = &Apache::lonnet::get('environment',['msgforward'],
                                          $domain,$user);
       my $msgforward=$userenv{'msgforward'};
       if ($msgforward) {
          foreach my $addr (split(/\,/,$msgforward)) {
    my ($forwuser,$forwdomain)=split(/\:/,$addr);
            push(@status,
         &user_crit_msg_raw($forwuser,$forwdomain,$subject,$message,
    $sendback,$toperm,$sentmessage,$nosentstore,
                                    $recipid,$attachmenturl,$permresults,$senthide));
          }
       } else { 
    push(@status,
        &user_crit_msg_raw($user,$domain,$subject,$message,$sendback,
    $toperm,$sentmessage,$nosentstore,$recipid,
                                   $attachmenturl,$permresults,$senthide));
       }
       if (wantarray) {
    return @status;
       }
       return join(' ',@status);
   }
   
   
 sub user_crit_received {  sub user_crit_received {
     my $msgid=shift;      my $msgid=shift;
     my %message=&Apache::lonnet::get('critical',[$msgid]);      my %message=&Apache::lonnet::get('critical',[$msgid]);
     my %contents=&unpackagemsg($message{$msgid});      my %contents=&unpackagemsg($message{$msgid},1);
     my $status='rec: '.      my $destname = $contents{'sendername'};
      &user_normal_msg($contents{'sendername'},$contents{'senderdomain'},      my $destdom = $contents{'senderdomain'};
                      'Receipt: '.$ENV{'user.name'}.' at '.$ENV{'user.domain'},      if ($contents{'replytoaddr'}) {
                      'User '.$ENV{'user.name'}.' at '.$ENV{'user.domain'}.          my ($repname,$repdom) = split(/:/,$contents{'replytoaddr'});
                      ' acknowledged receipt of message "'.          if (&Apache::lonnet::homeserver($repname,$repdom) ne 'no_host') {
                      $contents{'subject'}.'" dated '.$contents{'time'}.".\n\n"              $destname = $repname;
                      .'Message ID: '.$contents{'msgid'});              $destdom = $repdom;    
           }
       }
       my $status='rec: '.($contents{'sendback'}?
        &user_normal_msg($destname,$destdom,&mt('Receipt').': '.$env{'user.name'}.
                         ' '.&mt('at').' '.$env{'user.domain'}.', '.
                         $contents{'subject'},&mt('User').' '.$env{'user.name'}.
                         ' '.&mt('at').' '.$env{'user.domain'}.
                         ' acknowledged receipt of message'."\n".'   "'.
                         $contents{'subject'}.'"'."\n".&mt('dated').' '.
                         $contents{'time'}.".\n"
                         ):'no msg req');
     $status.=' trans: '.      $status.=' trans: '.
      &Apache::lonnet::put(       &Apache::lonnet::put(
      'nohist_email',{$contents{'msgid'} => $message{$msgid}});       'nohist_email',{$contents{'msgid'} => $message{$msgid}});
     $status.=' del: '.      $status.=' del: '.
      &Apache::lonnet::del('critical',[$contents{'msgid'}]);       &Apache::lonnet::del('critical',[$contents{'msgid'}]);
     &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},      &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
                          $ENV{'user.home'},'Received critical message '.                           $env{'user.home'},'Received critical message '.
                          $contents{'msgid'}.                           $contents{'msgid'}.
                          ', '.$status);                           ', '.$status);
     return $status;      return $status;
 }  }
   
 # ======================================================== Normal communication  
   
 sub user_normal_msg {  
     my ($user,$domain,$subject,$message,$citation)=@_;  
   sub user_normal_msg_raw {
       my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl,
           $toperm,$currid,$newid,$sentmessage,$crsmsgid,$symb,$restitle,
           $error,$nosentstore,$recipid,$permresults,$senthide)=@_;
 # Check if allowed missing  # Check if allowed missing
     my $status='';      my ($status,$packed_message);
     my $msgid='undefined';      my $msgid='undefined';
       my $text=$message;
     unless (($message)&&($user)&&($domain)) { $status='empty'; };      unless (($message)&&($user)&&($domain)) { $status='empty'; };
     my $homeserver=&Apache::lonnet::homeserver($user,$domain);      my $homeserver=&Apache::lonnet::homeserver($user,$domain);
     if ($homeserver ne 'no_host') {      if ($homeserver ne 'no_host') {
        my $msgid;         ($msgid,$packed_message)=
        ($msgid,$message)=&packagemsg($subject,$message,$citation);                   &packagemsg($subject,$message,$citation,$baseurl,
        $status=&Apache::lonnet::critical(                                       $attachmenturl,$user,$domain,$currid,
            'put:'.$domain.':'.$user.':nohist_email:'.                                       undef,$crsmsgid,$symb,$error,$recipid);
            &Apache::lonnet::escape($msgid).'='.  
            &Apache::lonnet::escape($message),$homeserver);  # Store in user folder
     } else {         $status=
      &Apache::lonnet::cput('nohist_email',{$msgid => $packed_message},
    $domain,$user);
   # Save new message received time
          &Apache::lonnet::put
                            ('email_status',{'recnewemail'=>time},$domain,$user);
   # Into sent-mail folder if sent mail storage required
          if (!$nosentstore) {
              my ($sentmsgid,$packed_message_no_citation) =
                  &packagemsg($subject,$message,undef,$baseurl,$attachmenturl,
                              $user,$domain,$currid,undef,$crsmsgid,$symb,$error,
                              undef,$senthide,$msgid);
              if ($status eq 'ok' || $status eq 'con_delayed') {
                  if ($senthide && $sentmsgid) {
                      &store_sent_mail($sentmsgid,$packed_message_no_citation);
                  } else {
                      &store_sent_mail($msgid,$packed_message_no_citation);
                  }
              }
          }
          if (ref($newid) eq 'SCALAR') {
      $$newid = $msgid;
          }
          if (ref($sentmessage) eq 'SCALAR') {
      $$sentmessage = $packed_message;
          }
   # Notifications
          my %userenv = &Apache::loncommon::getemails($user,$domain);
          my $notify = $userenv{'notification'};
          my $permemail = $userenv{'permanentemail'};
          my $numnotify = 0;
          my $numperm = 0;
          my $permlogmsgstatus;
          if ($notify) {
              $numnotify = &sendnotification($notify,$user,$domain,$subject,0,$text,$msgid,$attachmenturl);
          }
          if ($toperm && $permemail) {
              if ($notify && $numnotify) {
                  if (grep(/^\Q$permemail\E/,split(/,/,$notify))) {
                      $numperm = 1;
                  }
              }
              unless ($numperm) {
                  $numperm = &sendnotification($permemail,$user,$domain,$subject,0,
                                               $text,$msgid,$attachmenturl);
              }
          }
          if ($toperm) {
              $permlogmsgstatus = '. Perm. email log status '.
                            &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
                                                 "Perm. e-mail count $numperm for $user at $domain");
              if (ref($permresults) eq 'HASH') {
                  $permresults->{"$user:$domain"} = $numperm;
              }
          }
          &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
       $env{'user.home'},
       'Sending '.$msgid.' to '.$user.' at '.$domain.' with status '.$status.
                               $permlogmsgstatus);
      } else {
        $status='no_host';         $status='no_host';
     }     }
     &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},  
                          $ENV{'user.home'},  
       'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status);  
     return $status;      return $status;
 }  }
   
 # =============================================================== Status Change  sub user_normal_msg {
       my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl,
 sub statuschange {   $toperm,$sentmessage,$symb,$restitle,$error,$nosentstore,$recipid,
     my ($msgid,$newstatus)=@_;          $permresults,$senthide)=@_;
     my %status=&Apache::lonnet::get('email_status',[$msgid]);      my @status;
     if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; }      my %userenv = &Apache::lonnet::get('environment',['msgforward'],
     unless ($status{$msgid}) { $status{$msgid}='new'; }                                         $domain,$user);
     unless (($status{$msgid} eq 'replied') ||       my $msgforward=$userenv{'msgforward'};
             ($status{$msgid} eq 'forwarded')) {      if ($msgforward) {
  &Apache::lonnet::put('email_status',{$msgid => $newstatus});          foreach my $fwd (split(/\,/,$msgforward)) {
     }      my ($forwuser,$forwdomain)=split(/\:/,$fwd);
     if (($newstatus eq 'deleted') || ($newstatus eq 'new')) {      push(@status,
  &Apache::lonnet::put('email_status',{$msgid => $newstatus});          &user_normal_msg_raw($forwuser,$forwdomain,$subject,$message,
        $citation,$baseurl,$attachmenturl,$toperm,
        undef,undef,$sentmessage,undef,$symb,
                                        $restitle,$error,$nosentstore,$recipid,
                                        $permresults,$senthide));
           }
       } else {
    push(@status,&user_normal_msg_raw($user,$domain,$subject,$message,
        $citation,$baseurl,$attachmenturl,$toperm,
        undef,undef,$sentmessage,undef,$symb,
                                        $restitle,$error,$nosentstore,$recipid,
                                        $permresults,$senthide));
     }      }
       if (wantarray) {
           return @status;
       }
       return join(' ',@status);
 }  }
   
 # ======================================================= Display a course list  sub process_sent_mail {
       my ($msgsubj,$subj_prefix,$numsent,$stamp,$msgname,$msgdom,$msgcount,
 sub discourse {          $context,$pid,$savemsg,$recusers,$recudoms,$baseurl,$attachmenturl,
     my $r=shift;          $symb,$error,$senderuname,$senderdom,$recipid) = @_;
     my %courselist=&Apache::lonnet::dump(      my $sentsubj;
                    'classlist',      if ($numsent > 1) {
    $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},          $sentsubj = $subj_prefix.' ('.$numsent.' sent) '.$msgsubj;
    $ENV{'course.'.$ENV{'request.course.id'}.'.num'});      } else {
     my $now=time;          if ($subj_prefix) {
     $r->print(<<ENDDISHEADER);              $sentsubj = $subj_prefix.' ';
 <input type=hidden name=sendmode value=group>  
 <script>  
     function checkall() {  
  for (i=0; i<document.forms.compemail.elements.length; i++) {  
             if   
           (document.forms.compemail.elements[i].name.indexOf('send_to_')==0) {  
       document.forms.compemail.elements[i].checked=true;  
             }  
         }  
     }  
   
     function uncheckall() {  
  for (i=0; i<document.forms.compemail.elements.length; i++) {  
             if   
           (document.forms.compemail.elements[i].name.indexOf('send_to_')==0) {  
       document.forms.compemail.elements[i].checked=false;  
             }  
         }  
     }  
 </script>  
 <input type=button onClick="checkall()" value="Check for All">  
 <input type=button onClick="uncheckall()" value="Check for None">  
 <p>  
 ENDDISHEADER  
     map {  
         my ($end,$start)=split(/\:/,$courselist{$_});  
         my $active=1;  
         if (($end) && ($now>$end)) { $active=0; }  
         if ($active) {  
            my ($sname,$sdom)=split(/\:/,$_);  
            my %reply=&Apache::lonnet::get('environment',  
               ['firstname','middlename','lastname','generation'],  
               $sdom,$sname);  
            $r->print('<br><input type=checkbox name="send_to_'.$_.'"> '.  
       $reply{'firstname'}.' '.   
                       $reply{'middlename'}.' '.  
                       $reply{'lastname'}.' '.  
                       $reply{'generation'}.  
                       ' ('.$_.')');  
         }   
     } sort keys %courselist;  
 }  
   
 # ==================================================== Display Critical Message  
   
 sub discrit {  
     my $r=shift;  
       $r->print('<h1><font color=red>Critical Messages</font></h1>'.  
          '<form action=/adm/email method=post>'.  
          '<input type=hidden name=confirm value=true>');  
       my %what=&Apache::lonnet::dump('critical');  
       map {  
           my %content=&unpackagemsg($what{$_});  
           $content{'message'}=~s/\n/\<br\>/g;  
   $r->print('<hr>From: <b>'.$content{'sendername'}.'@'.  
                     $content{'senderdomain'}.'</b> ('.$content{'time'}.  
                     ')<br><blockquote>'.$content{'message'}.'</blockquote>'.  
   '<input type=submit name="rec_'.$_.'" value="Confirm Receipt">'.  
  '<input type=submit name="reprec_'.$_.'" value="Confirm Receipt and Reply">');  
       } sort keys %what;  
       $r->print(  
           '<input type=hidden name="displayedcrit" value="true"></form>');  
 }  
   
 # =============================================================== Compose reply  
   
 sub comprep {  
     my ($r,$msgid)=@_;  
       my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
       my %content=&unpackagemsg($message{$msgid});  
       my $quotemsg='> '.$content{'message'};  
       $quotemsg=~s/\r/\n/g;  
       $quotemsg=~s/\f/\n/g;  
       $quotemsg=~s/\n+/\n\> /g;  
       my $subject='Re: '.$content{'subject'};  
       my $dispcrit='';  
       if (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {  
          $dispcrit=  
      '<input type=checkbox name=critmsg> Send as critical message<p>';  
       }  
       $r->print(<<"ENDREPLY");  
 <form action="/adm/email" method=post>  
 <input type=hidden name=sendreply value="$msgid">  
 Subject: <input type=text size=50 name=subject value="$subject"><p>  
 <textarea name=message cols=60 rows=10>  
 $quotemsg  
 </textarea><p>  
 $dispcrit  
 <input type=submit value="Send Reply">  
 </form>  
 ENDREPLY  
 }  
   
 # ======================================================== Display all messages  
   
 sub disall {  
     my $r=shift;  
     $r->print('<h1>Display All Messages</h1>'.  
      '<table border=2><tr><th colspan=2>&nbsp</th><th>Date</th>'.  
      '<th>Username</th><th>Domain</th><th>Subject</th><th>Status</th></tr>');  
     map {  
         my ($sendtime,$shortsubj,$fromname,$fromdomain,$status)=  
     &Apache::lonmsg::unpackmsgid($_);  
        unless ($status eq 'deleted') {  
         if ($status eq 'new') {  
     $r->print('<tr bgcolor="#FFBB77">');  
         } elsif ($status eq 'read') {  
     $r->print('<tr bgcolor="#BBBB77">');  
         } elsif ($status eq 'replied') {  
     $r->print('<tr bgcolor="#AAAA88">');  
  } else {  
     $r->print('<tr bgcolor="#99BBBB">');  
         }          }
         $r->print('<td><a href="/adm/email?display='.$_.          $sentsubj .= $msgsubj;
                   '">Open</a></td><td><a href="/adm/email?markdel='.$_.      }
                   '">Delete</a></td><td>'.localtime($sendtime).'</td><td>'.      $sentsubj = &HTML::Entities::encode($sentsubj,'<>&"');
                   $fromname.'</td><td>'.$fromdomain.'</td><td>'.      my $sentmsgid = 
       &Apache::lonnet::unescape($shortsubj).'</td><td>'.          &buildmsgid($stamp,$sentsubj,$msgname,$msgdom,$msgcount,$context,$pid);
                       $status.'</td></tr>');      (undef,my $sentmessage) =
        }          &packagemsg($msgsubj,$savemsg,undef,$baseurl,$attachmenturl,$recusers,
     } sort split(/\&/,&Apache::lonnet::reply('keys:'.                      $recudoms,$sentmsgid,undef,undef,$symb,$error,$recipid);
  $ENV{'user.domain'}.':'.      my $status = &store_sent_mail($sentmsgid,$sentmessage,$senderuname,
                                         $ENV{'user.name'}.':nohist_email',                                    $senderdom);
                                         $ENV{'user.home'}));      return $status;
     $r->print('</table></body></html>');  }
 }  
   
 # ============================================================== Compose output  
   
 sub compout {  
     my ($r,$forwarding,$broadcast)=@_;  
       my $dispcrit='';  
     my $dissub='';  
     my $dismsg='';  
     my $func='Send New';  
       if (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {  
          $dispcrit=  
      '<input type=checkbox name=critmsg> Send as critical message<p>';  
       }  
     if ($forwarding) {  
        $dispcrit.='<input type=hidden name=forwid value="'.  
    $forwarding.'">';  
        $func='Forward';  
       my %message=&Apache::lonnet::get('nohist_email',[$forwarding]);  
       my %content=&unpackagemsg($message{$forwarding});  
   
        $dissub='Forwarding: '.$content{'subject'};  
        $dismsg='Forwarded message from '.  
    $content{'sendername'}.' at '.$content{'senderdomain'};  
     }  
     my $defdom=$ENV{'user.domain'};  
       $r->print('<form action="/adm/email"  name="compemail" method=post>'.  
                 '<input type=hidden name=sendmail value=on><table>');  
     unless ($broadcast eq 'group') {  
        $r->print(<<"ENDREC");  
 <table>  
 <tr><td>Username:</td><td><input type=text size=12 name=recuname></td></tr>  
 <tr><td>Domain:</td>  
 <td><input type=text size=12 name=recdomain value="$defdom"></td></tr>  
 ENDREC  
     }  
     $r->print(<<"ENDCOMP");  
 <tr><td>Subject:</td><td><input type=text size=50 name=subject value="$dissub">  
 </td></tr></table>  
 <textarea name=message cols=60 rows=10>$dismsg  
 </textarea><p>  
 $dispcrit  
 <input type=submit value="$func Mail">  
 ENDCOMP  
     if ($broadcast eq 'group') {  
        &discourse;  
     }  
     $r->print('</form>');  
 }  
   
 # ===================================================================== Handler  
   
 sub handler {  
     my $r=shift;  
   
 # ----------------------------------------------------------- Set document type  
   
   $r->content_type('text/html');  
   $r->send_http_header;  
   
   return OK if $r->header_only;  
   
 # --------------------------- Get query string for limited number of parameters  
   
     map {  
        my ($name, $value) = split(/=/,$_);  
        $value =~ tr/+/ /;  
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;  
        if (($name eq 'display') || ($name eq 'replyto') ||   
            ($name eq 'forward') || ($name eq 'markread') ||  
            ($name eq 'markdel') || ($name eq 'markunread') ||  
            ($name eq 'sendreply') || ($name eq 'compose') ||  
            ($name eq 'sendmail') || ($name eq 'critical')) {  
            unless ($ENV{'form.'.$name}) {  
               $ENV{'form.'.$name}=$value;  
    }  
        }  
     } (split(/&/,$ENV{'QUERY_STRING'}));  
   
 # --------------------------------------------------------------- Render Output  
     
   $r->print('<html><head><title>EMail and Messaging</title></head>');  
   $r->print(  
    '<body bgcolor="#FFFFFF"><img align=right src=/adm/lonIcons/lonlogos.gif>');  
   $r->print('<h1>EMail</h1>');  
   if ($ENV{'form.display'}) {  
       my $msgid=$ENV{'form.display'};  
       &statuschange($msgid,'read');  
       my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
       my %content=&unpackagemsg($message{$msgid});  
       $r->print('<b>Subject:</b> '.$content{'subject'}.  
              '<br><b>From:</b> '.$content{'sendername'}.' at '.  
                                  $content{'senderdomain'}.  
              '<br><b>Time:</b> '.$content{'time'}.'<p>'.  
              '<table border=2><tr bgcolor="#FFFFAA"><td>Functions:</td>'.  
            '<td><a href="/adm/email?replyto='.&Apache::lonnet::escape($msgid).  
              '"><b>Reply</b></a></td>'.  
            '<td><a href="/adm/email?forward='.&Apache::lonnet::escape($msgid).  
              '"><b>Forward</b></a></td>'.  
         '<td><a href="/adm/email?markunread='.&Apache::lonnet::escape($msgid).  
              '"><b>Mark Unread</b></a></td>'.  
         '<td><a href="/adm/email"><b>Display all Messages</b></a></td>'.  
              '</tr></table><p><pre>'.  
              $content{'message'}.'</pre><hr>'.$content{'citation'});  
   } elsif ($ENV{'form.replyto'}) {  
       &comprep($r,$ENV{'form.replyto'});  
   } elsif ($ENV{'form.sendreply'}) {  
       my $msgid=$ENV{'form.sendreply'};  
       my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
       my %content=&unpackagemsg($message{$msgid});  
       &statuschange($msgid,'replied');  
       if (($ENV{'form.critmsg'}) &&   
           (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) {  
          $r->print('Sending critical: '.  
                 &user_crit_msg($content{'sendername'},  
                                  $content{'senderdomain'},  
                                  $ENV{'form.subject'},  
                                  $ENV{'form.message'}));  
       } else {  
          $r->print('Sending: '.&user_normal_msg($content{'sendername'},  
                                  $content{'senderdomain'},  
                                  $ENV{'form.subject'},  
                                  $ENV{'form.message'}));  
       }  
       if ($ENV{'form.displayedcrit'}) {  
           &discrit($r);  
       } else {  
   &disall($r);  
       }  
   } elsif ($ENV{'form.confirm'}) {  
       map {  
           if ($_=~/^form\.rec\_(.*)$/) {  
       $r->print('<b>Confirming Receipt:</b> '.  
                         &user_crit_received($1).'<br>');  
           }  
           if ($_=~/^form\.reprec\_(.*)$/) {  
               my $msgid=$1;  
       $r->print('<b>Confirming Receipt:</b> '.  
                         &user_crit_received($msgid).'<br>');  
               &comprep($r,$msgid);  
           }  
       } keys %ENV;  
       &discrit($r);  
   } elsif ($ENV{'form.critical'}) {  
       &discrit($r);  
   } elsif ($ENV{'form.forward'}) {  
       &compout($r,$ENV{'form.forward'});  
   } elsif ($ENV{'form.markread'}) {  
   } elsif ($ENV{'form.markdel'}) {  
       &statuschange($ENV{'form.markdel'},'deleted');  
       &disall($r);  
   } elsif ($ENV{'form.markunread'}) {  
       &statuschange($ENV{'form.markunread'},'new');  
       &disall($r);  
   } elsif ($ENV{'form.compose'}) {  
       &compout($r,'',$ENV{'form.compose'});  
   } elsif ($ENV{'form.sendmail'}) {  
       my %content=();  
       undef %content;  
       if ($ENV{'form.forwid'}) {  
         my $msgid=$ENV{'form.forwid'};  
         my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
         %content=&unpackagemsg($message{$msgid});  
         &statuschange($msgid,'forwarded');  
         $ENV{'form.message'}.="\n\n-- Forwarded message --\n\n".  
                        $content{'message'};  
       }  
       my %toaddr=();  
       undef %toaddr;  
       if ($ENV{'form.sendmode'} eq 'group') {  
           map {  
       if ($_=~/^form\.send\_to\_(.+)$/) {  
   $toaddr{$1}=1;  
               }  
           } keys %ENV;  
       } else {  
   $toaddr{$ENV{'form.recuname'}.':'.$ENV{'form.recdomain'}}=1;  
       }  
     map {  
       my ($recuname,$recdomain)=split(/\:/,$_);  
       if (($ENV{'form.critmsg'}) &&   
           (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) {  
          $r->print('Sending critical: '.  
                 &user_crit_msg($recuname,$recdomain,  
                                  $ENV{'form.subject'},  
                                  $ENV{'form.message'},  
                                  $content{'citation'}));  
       } else {  
          $r->print('Sending: '.&user_normal_msg($recuname,$recdomain,  
                                  $ENV{'form.subject'},  
                                  $ENV{'form.message'},  
                                  $content{'citation'}));  
       }  
       $r->print('<br>');  
     } keys %toaddr;  
       if ($ENV{'form.displayedcrit'}) {  
           &discrit($r);  
       } else {  
   &disall($r);  
       }  
   } else {  
       &disall($r);  
   }  
   $r->print('</body></html>');  
   return OK;  
   
   sub store_sent_mail {
       my ($msgid,$message,$senderuname,$senderdom) = @_;
       if ($senderuname eq '') {
           $senderuname = $env{'user.name'};
       }
       if ($senderdom eq '') {
           $senderdom = $env{'user.domain'};
       }
       my $status =' '.&Apache::lonnet::cput('nohist_email_sent',
     {$msgid => $message},
     $senderdom,$senderuname);
       return $status;
 }  }
 # ================================================= Main program, reset counter  
   
 sub BEGIN {  sub store_recipients {
     $msgcount=0;      my ($subject,$sendername,$senderdom,$reciphash) = @_;
       my $context = &get_course_context();
       my $now = time();
       my $msgcount = &get_uniq();
       my $recipid =
           &buildmsgid($now,$subject,$sendername,$senderdom,$msgcount,$context,$$);
       my %recipinfo = (
                            $recipid => $reciphash,
                       );
       my $status = &Apache::lonnet::put('nohist_emailrecip',\%recipinfo,
                                         $senderdom,$sendername); 
       if ($status eq 'ok') {
           return ($recipid,$status);
       } else {
           return (undef,$status);
       }
 }  }
   
 1;  
 __END__  
   
   sub foldersuffix {
       my $folder=shift;
       unless ($folder) { return ''; }
       my $suffix;
       my %folderhash = &get_user_folders($folder);
       if (ref($folderhash{$folder}) eq 'HASH') {
           $suffix = '_'.&escape($folderhash{$folder}{'id'});
       } else {
           $suffix = '_'.&escape($folder);
       }
       return $suffix;
   }
   
   
   sub get_user_folders {
       my ($folder) = @_;
       my %userfolders = 
             &Apache::lonnet::dump('email_folders',undef,undef,$folder);
       my $lock = "\0".'lock_counter'; # locks db while counter incremented
       my $counter = "\0".'idcount';   # used in suffix for email db files
       if (defined($userfolders{$lock})) {
           delete($userfolders{$lock});
       }
       if (defined($userfolders{$counter})) {
           delete($userfolders{$counter});
       }
       return %userfolders;
   }
   
   sub secapply {
       my $rec=shift;
       my $defaultflag=shift;
       $rec=~s/\s+//g;
       unless ($rec =~ /\:/) {
           $rec=~s/\@/\:/g;
       }
       my ($adr,$sections_or_groups)=($rec=~/^([^\(]+)\(([^\)]+)\)/);
       if ($sections_or_groups) {
    foreach my $item (split(/\;/,$sections_or_groups)) {
               if (($item eq $env{'request.course.sec'}) ||
                   ($defaultflag && ($item eq '*'))) {
                   return $adr; 
               } elsif ($env{'request.course.groups'}) {
                   my @usersgroups = split(/:/,$env{'request.course.groups'});
                   if (grep(/^\Q$item\E$/,@usersgroups)) {
                       return $adr;
                   }
               } 
           }
       } else {
          return $rec;
       }
       return '';
   }
   
   sub decide_receiver {
       my ($feedurl,$author,$question,$course,$policy,$defaultflag) = @_;
       &Apache::lonenc::check_decrypt(\$feedurl);
       my $typestyle='';
       my %to=();
       if ($env{'form.discuss'} eq 'author' ||$author) {
    $typestyle.='Submitting as Author Feedback<br />';
    $feedurl=~ m{^/res/($LONCAPA::domain_re)/($LONCAPA::username_re)/};
    $to{$2.':'.$1}=1;
       }
       my $cid = $env{'request.course.id'};
       if ($env{'form.discuss'} eq 'question' ||$question) {
    $typestyle.=&mt('Submitting as Question').'<br />';
    foreach my $item (split(/\,/,$env{'course.'.$cid.'.question.email'})) {
       my $rec=&secapply($item,$defaultflag);
       if ($rec) { $to{$rec}=1; }
    } 
       }
       if ($env{'form.discuss'} eq 'course' ||$course) {
    $typestyle.=&mt('Submitting as Comment').'<br />';
    foreach my $item (split(/\,/,$env{'course.'.$cid.'.comment.email'})) {
       my $rec=&secapply($item,$defaultflag);
       if ($rec) { $to{$rec}=1; }
    } 
       }
       if ($env{'form.discuss'} eq 'policy' ||$policy) {
    $typestyle.=&mt('Submitting as Policy Feedback').'<br />';
    foreach my $item (split(/\,/,$env{'course.'.$cid.'.policy.email'})) {
       my $rec=&secapply($item,$defaultflag);
       if ($rec) { $to{$rec}=1; }
    } 
       }
       if ((scalar(%to) eq '0') && (!$defaultflag)) {
    ($typestyle,%to)=
       &decide_receiver($feedurl,$author,$question,$course,$policy,1);
       }
       return ($typestyle,%to);
   }
   
   1;
   __END__
   

Removed from v.1.18  
changed lines
  Added in v.1.248


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