# vim: set cindent expandtab ts=4 sw=4:
#
# Copyright (c) 1998-2005 Chi-Keung Ho. All rights reserved.
#
# This programe 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.
#
# ExtMan - web interface to manage virtual accounts
# $Id$
package Ext::MgrApp::GroupMail;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter Ext::MgrApp);
use POSIX qw(strftime);
use Ext::Utils; # import url2str
use Ext::MgrApp;
use Ext::RFC822;
use MIME::Base64;
use Ext::CGI; # import $CRLF
use vars qw($lang_charset %lang_groupmail);
use Ext::Lang;
sub init {
my $self = shift;
$self->SUPER::init(@_);
return unless($self->valid||$self->permit);
$self->add_methods(edit_mail => \&edit_mail);
$self->add_methods(send_mail => \&send_mail);
$self->{default_mode} = 'edit_mail';
$self->_initme;
$self;
}
sub _initme {
initlang($_[0]->{sysconfig}->{'SYS_LANG'}, __PACKAGE__);
$_[0]->{tpl}->assign( lang_charset => $lang_charset );
$_[0]->{tpl}->assign( \%lang_groupmail );
}
sub edit_mail {
my $self = shift;
my $tpl = $self->{tpl};
my $mgr = $self->{backend};
my $q = $self->{query};
my $sys = $self->{sysconfig};
my $domains = [];
if ($ENV{USERTYPE} eq 'admin') {
my $alldomain = $mgr->get_domains_list || [];
foreach my $d ( @$alldomain ) {
push @$domains, $d->{domain};
}
} else {
my $pm = $mgr->get_manager_info($ENV{USERNAME});
$domains = $pm->{domain};
}
if ($domains) {
$domains = [$domains] unless (ref $domains);
foreach my $vd ( @$domains ) {
$tpl->assign(
'LOOP_DOMAIN',
DOMAIN => $vd
);
}
} else {
# no permission or not assign domain
$tpl->assign(NOPERM => 1);
$tpl->assign(HAVE_USER => 0);
}
$tpl->assign(
SUBJECT => $q->cgi('subject'),
BODY => $q->cgi('body'),
RECIPIENT => $q->cgi('recipient'),
);
}
sub send_mail {
my $self = shift;
my $tpl = $self->{tpl};
my $mgr = $self->{backend};
my $q = $self->{query};
my $charset = $lang_charset; # infact it's always utf-8
my $recip = $q->cgi('recipient');
my $subject = $q->cgi('subject');
my $body = $q->cgi('body');
my $domain = $q->cgi('domain');
my $all = $q->cgi('alldomain') ? 1 : 0;
# permission validation for specific domain
unless ($self->valid_perm($domain)) {
$self->error('Access denied');
return 0;
}
my $addrs = [];
my $rc = '';
if ($all) {
if ($ENV{USERTYPE} eq 'admin') { # supper user
my $ds = $mgr->get_domains_list || [];
foreach my $vd (@$ds) {
my $us = $mgr->get_users_list($vd->{domain}) || [];
foreach my $u (@$us) {
push @$addrs, $u->{mail};
}
}
} else {
my $ref = $self->manager_owndomain($ENV{USERNAME});
foreach my $vd ( @$ref ) {
my $us = $mgr->get_users_list($vd) || [];
foreach my $u (@$us) {
push @$addrs, $u->{mail};
}
}
}
} else {
$recip =~ s![^a-zA-Z0-9-_\.=,\n@]!!g;
$recip =~ s!\r!!g;
$recip =~ s! !!g;
if ($recip =~ m#^[\n\s]*@[\n\s]*#) {
# only contain @, means group send to a domain
my $us = $mgr->get_users_list($domain) || [];
foreach my $u (@$us) {
push @$addrs, $u->{mail};
}
} elsif ($recip =~ m#^[^\@]+$#) {
foreach my $r (split(/\s+/, $mgr->mshack($recip))) {
push @$addrs, "$r\@$domain";
}
} else {
$tpl->assign(ERROR => $lang_groupmail{'errinput'});
$self->edit_mail;
return 0;
}
}
$rc = $self->send_groupmail($addrs);
if ($rc) {
$tpl->assign(ERROR => $rc);
} else {
$tpl->assign(SUCCESS => $lang_groupmail{'okmail'});
}
$self->edit_mail;
}
sub send_groupmail {
my $self = shift;
my $addrs = $_[0]; # must ref
my $num = scalar @$addrs;
my $limit = $self->{sysconfig}->{SYS_PERMAIL_LIMIT} || '100';
my $buf = [];
return $lang_groupmail{'noaddrs'} unless (scalar @$addrs);
while (my $addr = pop @$addrs) {
if (scalar @$buf < $limit) {
push @$buf, $addr;
} else {
my $rc = $self->_mail($buf);
$buf = []; # cleanup
push @$buf, $addr; # still need to add!
return $rc if ($rc);
}
}
if (scalar @$buf > 0) {
# still have some recip
my $rc = $self->_mail($buf);
return $rc if ($rc);
}
'0'; # default to success
}
sub _mail {
my $self = shift;
my $buf = $_[0]; # must ref
my $q = $self->{query};
my $chst = $self->{sysconfig}->{SYS_CHARSET} || 'us-ascii';
my $sender = $self->{sysconfig}->{SYS_GROUPMAIL_SENDER} || $ENV{USERNAME};
my $sendmail = "/usr/sbin/sendmail -oi -t -f \"$sender\"";
# Code from ExtMail
local (*RFH, *WFH);
my ($pid, $open1);
my $errbuf;
eval {
require IPC::Open2;
IPC::Open2->import(qw(open2));
# 2>&1 will redirect the stderr to stdout, then we can
# use open2 to capture stderr, but waiting for a better
# way to capture, like c dup(), dup2(), pipe()
$pid = open2(\*RFH, \*WFH, "$sendmail 2>&1") or
die "open2() fail, reason: $!\n";
};
# open1 means the normal perl open(), if open1 null, means
# IPC::Open2 is available, use it instead
$open1 = $@;
if ($open1) {
open(WFH, "|$sendmail") or
die $lang_groupmail{'errmail'}."broken pipe: $!\n";
}
my $body = $q->cgi('body');
my $html = $q->cgi('html');
my $boundary;
my $attach = $q->cgi('UPLOAD_FILES') ? 1: 0;
my $type = ($attach ? 'multipart/mixed' : ($html ? 'text/html' : 'text/plain'));
$boundary = sprintf "=_%s_%s_%s", int(rand(100)), $$, time if ($attach);
print WFH 'From: "'.rfc822_encode_str($chst, $sender)."\" <$sender>\n";
print WFH 'Bcc: '.rfc822_encode_addr($chst,join(',',@$buf))."\n";
print WFH 'Subject: '.rfc822_encode_str($chst,$q->cgi('subject'))."\n";
print WFH "To: \"NO-REPLY\" <>\n"; # NULL
print WFH 'Date: '.rfc822_date($self->{sysconfig}->{SYS_TIMEZONE})."\n";
print WFH "Mime-Version: 1.0\n";
print WFH "X-Originating-Ip: [$ENV{REMOTE_ADDR}]\n";
print WFH "X-Mailer: ExtMan - GroupMail\n";
print WFH "Content-Type: $type; charset=$chst";
if ($attach) {
print WFH "; boundary=\"$boundary\"\n";
} else {
print WFH "\n";
}
print WFH "Content-Transfer-Encoding: 8bit\n\n";
if ($attach) {
my $type = ($html ? 'text/html' : 'text/plain');
print WFH "This is a MIME-formatted message. If you see this text it means that your\n";
print WFH "mail software cannot handle MIME-formatted messages.\n\n";
print WFH "--$boundary\n";
print WFH "Content-Type: $type; charset=\"$chst\";\n";
print WFH "Content-Transfer-Encoding: 8bit\n\n";
}
# it's the right time to parse template from body
my $list = '';
foreach my $l (@$buf) {
$list .= " $l\n";
}
$body =~ s!\$ALL!$list!g;
print WFH "$body\n";
if ($attach) {
my $lists = $q->cgi('UPLOAD_FILES');
foreach(keys %$lists) {
open(TFD, "< ".$lists->{$_}->{path})
or die "Attach open fail, $!\n";
my $old = $/;
local $/ = $CRLF.$CRLF;
my $hdr = <TFD>;
$hdr=~s/$CRLF/\n/g;
$/ = $old;
my %header = hdr_fmt_hash($hdr);
$header{filename} = _cvt2formal($header{filename});
print WFH "--$boundary\n";
print WFH "Content-Disposition: attachment; filename=\"$header{filename}\"\n";
print WFH 'Content-Type: '.$header{'Content-Type'}."; charset=\"$chst\"; name=\"$header{filename}\"\n";
print WFH "Content-Transfer-Encoding: base64\n\n";
while(read(TFD, my $buf, 60*57)) {
print WFH encode_base64($buf);
}
close TFD;
print WFH "\n"; # need?
}
print WFH "--$boundary--\n";
}
if ($open1) {
close WFH or $errbuf = "Send fail, return code $?";
} else {
close WFH;
while (<RFH>) {
$errbuf .= $_;
}
close RFH;
waitpid ($pid, 0);
}
if ($errbuf) {
$errbuf =~ s#\n#</br>\n#g;
die "$errbuf";
}
'0'; # default to success
}
sub _cvt2formal {
my $filename = shift;
if($filename=~/\\/) { # win32 filename, eg: c:\\doc\\test.gif
$filename=~ s#.*\\+([^\\]+)$#$1#;
}elsif($filename=~/\//) { # Unix path name, eg: /path/to/test.gif
$filename=~ s#.*\/+([^\/]+)$#$1#;
}else {
$filename=~ s#\s##g; # remove all space
}
$filename
}
sub hdr_fmt_hash {
my @a = hdr_fmt_list($_[0]);
my %head;
foreach(@a) {
next unless (ref $_ eq 'HASH');
foreach my $k (keys %$_) {
if($k=~/^Content/) {
my @temp = split(/; /,$$_{$k});
$head{$k} = $temp[0];
foreach(@temp) {
s/\t//g;
if(/=/) {
my($k,$v)=m/([a-zA-Z0-9-_]+)="*([^\"]*)"*/;
$head{$k} = $v if not defined $head{$k};
}
}
}else {# common header(may be in mail head
if(not defined $head{$k}) {
$head{$k} = $$_{$k};
}else {
if(ref $head{$k} eq 'ARRAY') {
push @{$head{$k}}, $$_{$k};
}else {
$head{$k} = [$head{$k}, $$_{$k}];
}
}
}
}
}
%head;
}
sub hdr_fmt_list {
my $s = $_[0];
$s =~ s/\n\s+/ /g; # cat \n\t or \n[:space]+ together
my @a = split(/\n/, $s);
foreach (0...(scalar @a-1)) {
my($k, $v) = ($a[$_]=~ m/^([^:]+):\s*(.*)\s*$/g);
next if (not defined $k);
if(defined $v and $v=~/=\?[^?]*\?[QB]\?[^?]*\?=/) {
$v=~s/(\?=)\s+(=\?)/$1$2/g; # cat multiple Q/B encode strs into one.
}
$a[$_] = {$k=>$v};
}
@a;
}
sub post_run {
my $template = $_[0]->{query}->cgi('screen') || 'edit_groupmail.html';
$_[0]->{tpl}->process($template);
$_[0]->{tpl}->print;
}
1;
syntax highlighted by Code2HTML, v. 0.9.1