# ==================================================================
# Gossamer List - enhanced mailing list management system
#
#   Website  : http://gossamer-threads.com/
#   Support  : http://gossamer-threads.com/scripts/support/
#   CVS Info :                          
#   Revision : $Id: Mailer.pm,v 1.79 2005/04/06 23:17:03 bao Exp $
#
# Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
# Redistribution in part or in whole strictly prohibited.  Please
# see LICENSE file for full details.
# ==================================================================
#

package GList::Mailer;

use strict;
use GList qw/:user_type :objects :tracks $DEBUG/;
use GT::AutoLoader;

sub process {
#---------------------------------------------------------------------
# Setermine what to do
#
    my $do = shift;

    my $action = _determine_action($do) or die "Error: Invalid Action! ($do)";
    my ($tpl, $results) = GT::Plugins->dispatch($CFG->{priv_path}.'/lib/GList/Plugins', $action, \&$action);

    $MN_SELECTED = 3;
    if ($tpl) {
        my $hidden = GList::hidden();
        $results->{hidden_query}   = $hidden->{hidden_query};
        $results->{hidden_objects} = $hidden->{hidden_objects};
        GList::display($tpl, $results);
    }
}

sub mli_print {
#--------------------------------------------------------------------
#
    my ($page, $args) = @_;

# Get category's information
    my $nav  = _load_navigator() || {};
    my ($info, $url);
    if ($IN->param('id')) {
        $info   = ( $USER->{usr_type} == ADMINISTRATOR ) ? $DB->table('CatMailing')->get({ cm_id => $IN->param('id') })
                                                         : $DB->table('CatMailing')->get({ cm_id => $IN->param('id'), cm_user_id_fk => $USER->{usr_username} });
    }
    $info ||= {};

# Create the URL
    my @items = ('cd', 'cs', 'ca');
    foreach (@items) {
        $url .= "$_=".$IN->param($_).'&'  if ($IN->param($_));
    }
    return ($page, { %$info, %$nav, %$args, url => $url });
}

$COMPILE{mli_home} = <<'END_OF_SUB';
sub mli_home {
#--------------------------------------------------------------------
# Print home page
#
    my ($msg, $cgi) = @_;

    $cgi ||= $IN->get_hash;
    $msg ||= GList::language('MLI_SUCCESS', $cgi->{sent})           if ($cgi->{sent});
    $msg   = GList::language('MLI_BOUNCED_EMAILS', $cgi->{bounced}) if ($cgi->{bounced});
    if ($cgi->{do} =~ /msg_send|mli_bounced/) {
        $cgi->{fd} = 1;
    }

#------------demo code-----------

    if (!$cgi->{d}) {
        if ( $cgi->{fd} and $cgi->{fd} =~ /^1|2/ ) { # Queue & Sent Items
            $cgi->{mli_delete}      = 1;
            $cgi->{'mli_delete-opt'}= '<';
            $cgi->{mli_done}        = 1;
            $cgi->{'mli_done-opt'}  = ( $cgi->{fd} == 2 ) ? '>' : '<';
            $cgi->{mli_scheduled}   = 0;
            if ( $cgi->{fd} == 2 and $cgi->{do} ne 'mli_search' ) {
                $cgi->{mli_cat_id_fk}   = ( $cgi->{id} ) ? $cgi->{id} : 0;
            }
        }
        elsif ( $cgi->{fd} == 3 ) { # Delete Items
            $cgi->{mli_delete} = 1;
        }
        elsif ($cgi->{fd} == 4) { # Scheduled mailings
            $cgi->{mli_scheduled} = 1;
            $cgi->{mli_delete}    = 0;
        }
    }
    my $search_check = ($IN->param('do') eq 'mli_search') ? 1 : 0;
    if ($cgi->{'mli_done-ge'} or $cgi->{'mli_done-le'}) {
        my $format  = $USER->{usr_date_format} || '%yyyy%-%mm%-%dd%';
        my ($valid_from, $valid_to) = (1, 1);

        require GT::Date;
        if ($cgi->{'mli_done-ge'}) {
            $valid_from = GList::date_to_time($cgi->{'mli_done-ge'}, $format);
            $cgi->{'mli_done-ge'} = GT::Date::date_get($valid_from, $format);
        }
        if ($cgi->{'mli_done-le'}) {
            $valid_to = GList::date_to_time($cgi->{'mli_done-le'}, $format);
            $cgi->{'mli_done-le'} = GT::Date::date_get($valid_to, $format);
        }

        if ($search_check and (!$valid_from or !$valid_to)) {
            $format =~ s/\%//g;
            return ('mli_search_form.html', { msg => GList::language('SYS_DATE_FORMAT_INVALID', uc GList::language('SYS_DATE_FORMAT')) });
        }
    }
    if ($IN->param('do') eq 'mli_search' and $IN->param('fd') and $IN->param('fd') !~ /^1|2|3/) {
        my @cat = split(/\-/, $IN->param('fd'));
        $cgi->{mli_cat_id_fk} = $cat[1];
    }
    my $results   = GList::search(
            cgi         => $cgi,
            db          => $DB->table('MailingIndex'),
            prefix      => 'mli',
            sb          => 'mli_id',
            so          => 'DESC',
            search_check=> $search_check
    );
# Get category's information
    my $info = {};
    if ($IN->param('id')) {
        $info = ( $USER->{usr_type} == ADMINISTRATOR ) ? $DB->table('CatMailing')->get({ cm_id => $IN->param('id') })
                                                       : $DB->table('CatMailing')->get({ cm_id => $IN->param('id'), cm_user_id_fk => $USER->{usr_username} });
        $info ||= {};
    }

    my $nav = _load_navigator() || {};

# Create the URL
    my $url;
    my @items = ('cd', 'cs', 'ca');
    foreach ( @items ) {
        $url .= "$_=".$cgi->{$_}.'&'  if ( $cgi->{$_} );
    }
    chop $url if ($url);

    if ( ref $results ne 'HASH' ) {
        ( $IN->param('do') eq 'mli_search' ) ? return ('mli_search_form.html', { msg => $msg || $results, %$nav, %$info, url => $url })
                                             : return ('mli_home.html',        { msg => $msg || $results, %$nav, %$info, url => $url });
    }
    elsif ( $results->{error} and $search_check ) {
        return ('mli_search_form.html', { msg => $results->{error} });
    }

    my $eml    = $DB->table('EmailMailings');
    my $output = $results->{results};

    require GT::SQL::Condition;
    foreach my $rs ( @$output ) {
        my $cd_sent = GT::SQL::Condition->new(
            eml_mailing_id_fk => '='  => $rs->{mli_id},
            eml_sent          => '<>' => 0
        );
        $rs->{total}          = $eml->count({ eml_mailing_id_fk => $rs->{mli_id} });
        $rs->{bounced_emails} = $eml->count({ eml_mailing_id_fk => $rs->{mli_id}, eml_bounced => 1 });
        $rs->{done}           = $eml->count($cd_sent);
    }
    $results->{msg} = $msg if ($msg);
    return ('mli_home.html', { %$results, %$nav, %$info, url => $url });
}
END_OF_SUB

$COMPILE{mli_search_form} = <<'END_OF_SUB';
sub mli_search_form {
#--------------------------------------------------------------------
# Print the search form
#
    my $msg = shift;
    return mli_print('mli_search_form.html', { msg => $msg });
}
END_OF_SUB

$COMPILE{mli_empty} = <<'END_OF_SUB';
sub mli_empty {
#--------------------------------------------------------------------
# Delete all of deleted items
#
    require GT::SQL::Condition;
    my $db = $DB->table('MailingIndex');
    my $cd = new GT::SQL::Condition;
    $cd->add('mli_delete', '=', 1);

    if ($USER->{usr_type} == ADMINISTRATOR and $IN->param('users')) { # As a admin user
        $cd->add('mli_user_id_fk', '<>', $USER->{usr_username});
    }
    else {
        $cd->add('mli_user_id_fk', '=', $USER->{usr_username});
    }
    $db->delete($cd);

    mli_home(GList::language('MLI_EMPTY'));
}
END_OF_SUB

$COMPILE{mli_delete} = <<'END_OF_SUB';
sub mli_delete {
#--------------------------------------------------------------------
# Delete the mailings
#
    return mli_home(GList::delete('MailingIndex', 'mli'));
}
END_OF_SUB

$COMPILE{mli_move} = <<'END_OF_SUB';
sub mli_move {
#--------------------------------------------------------------------
# Moves the records to another category
#
    ( $IN->param('modify') ) or return mli_home(GList::language('SYS_MOVE_ERR'));

# Check category ID
    my $to  = $IN->param('move_to');
    ( $to) or return mli_home(GList::language('SYS_TARGET_ERR'));

    if ( $to ne 'root' and $to ne 'draft' and $to ne 'sent') { # Move to a sub-category
        my $info = GList::check_owner('CatMailing', 'cm', $to);
        ( ref $info eq 'HASH' ) or return mli_home($info);
    }

# Need to know the number of records modified
    my $rec_modified = 0;
    my $rec_declined = 0;

    my $mod = ( ref $IN->param('modify') eq 'ARRAY' ) ? $IN->param('modify') : [$IN->param('modify')];
    my $db  = $DB->table('MailingIndex');
# For through the record numbers. These are the values of the check boxes
    foreach my $rec_num ( @$mod )  {
        my $change = {};
        $change->{mli_id} = $IN->param("$rec_num-mli_id") if ($IN->param("$rec_num-mli_id"));

# Check if users can modify only their own records except Administrator
        my $rs = $db->get($change);
        if ( $USER->{usr_type} != ADMINISTRATOR ) {
            next if ( !$rs );
            if ( $rs->{'mli_user_id_fk'} ne $USER->{usr_username} ) {
                $rec_declined++; next;
            }
        }
        next unless ( keys %$change );
        next if ($to eq 'draft' and $rs->{mli_done});
        next if ($to eq 'sent'  and !$rs->{mli_done});

        my $ret;
        if ( $to =~ /^root|draft|sent/mi ) {
            $ret = ( $IN->param('fd') == 3 ) ? $db->update({ mli_cat_id_fk => 0, mli_delete => '0' }, $change)
                                             : $db->update({ mli_cat_id_fk => 0 }, $change);
        }
        else {
            $ret = $db->update({ mli_cat_id_fk => $to }, $change);
        }
        $rec_modified++ if (defined $ret and $ret != 0);
    }
    mli_home(($rec_declined) ? GList::language('SYS_MOVED2', $rec_modified, $rec_declined) : GList::language('SYS_MOVED', $rec_modified));
}
END_OF_SUB


$COMPILE{mli_send} = __LINE__ . <<'END_OF_SUB';
sub mli_send {
#--------------------------------------------------------------------
# Send Email - Send email to subcribers
#
    return mli_home(GList::language('MLI_INVALID')) if (!$IN->param('modify'));

    $MN_SELECTED = 3;

# Check account limits
    my $num_sent = GList::check_limit('email30') || 0;
    return mli_home(GList::language('SYS_OVERLIMIT_EMAIL30')) if ($num_sent == 1);

    my $mod = (ref $IN->param('modify') eq 'ARRAY') ? $IN->param('modify') : [$IN->param('modify')];
    my @ids = map $IN->param("$_-mli_id"), @{$mod};

    my $total_size = _size_mailings(\@ids, 'web');
    return mli_home(GList::language('MLI_MSG_EMPTY')) if (!$total_size);

    _send('web', \@ids, $total_size);
    return;
}
END_OF_SUB

$COMPILE{mli_fview} = <<'END_OF_SUB';
sub mli_fview {
#--------------------------------------------------------------------
# Print a attached file
#
    return GList::view_file();
}
END_OF_SUB

$COMPILE{mli_fdownload} = <<'END_OF_SUB';
sub mli_fdownload {
#--------------------------------------------------------------------
# Print a attached file
#
    return GList::download_file();
}
END_OF_SUB

$COMPILE{mli_bounced_form} = <<'END_OF_SUB';
sub mli_bounced_form {
#--------------------------------------------------------------------
#
    my ($msg, $page) = @_;
    $page ||= 'mli_check_bounced_form.html';
    $page   = 'mli_check_bounced_results.html' if ($IN->param('results'));

    return mli_print($page, { msg => $msg });
}
END_OF_SUB

$COMPILE{mli_bounced} = <<'END_OF_SUB';
sub mli_bounced {
#--------------------------------------------------------------------
# To check a pop account and delete bounced emails
#

#------------demo code-----------

    return mli_bounced_form(GList::language('MLI_BOUNCED_NO_SERVER'))   unless ($IN->param('mail_host'));
    return mli_bounced_form(GList::language('MLI_BOUNCED_NO_USER'))     unless ($IN->param('mail_user'));
    return mli_bounced_form(GList::language('MLI_BOUNCED_NO_PASS'))     unless ($IN->param('mail_pass'));

    _bounced('web', {
        host      => $IN->param('mail_host'),
        port      => $IN->param('mail_port') || 110,
        user      => $IN->param('mail_user'),
        pass      => $IN->param('mail_pass'),
        auth_mode => 'PASS',
        debug     => $CFG->{debug_level}
    }, { delete => $IN->param('del_bounced'), save => $IN->param('save_info') });
}
END_OF_SUB

$COMPILE{mli_recipients} = <<'END_OF_SUB';
sub mli_recipients {
#-------------------------------------------------------------------
# View recipients
#
    my $id = $IN->param('eml_mailing_id_fk');
    return mli_home(GList::language('MLI_INVALID')) if (!$id);

# Check the record's onwer
    my $mli = $DB->table('MailingIndex')->get($id);
    return mli_home(GList::language('MLI_NOT_FOUND', $id)) if (!$mli);

    if ( $USER->{usr_type} != ADMINISTRATOR ) { # As a user
        my $cond = new GT::SQL::Condition('OR');

        $cond->add('usr_username', '=', $mli->{mli_user_id_fk});
        my $u = $DB->table('Users')->select($cond)->rows;
        return mli_home(GList::language('SYS_PER_DENIED')) if (!$u);
    }

    my $cgi = $IN->get_hash;
    $cgi->{eml_mailing_id_fk} = $id;
    my $results   = GList::search(
        cgi         => $cgi,
        db          => $DB->table('EmailMailings'),
        skip_user   => 1,
        prefix      => 'eml',
        sb          => 'eml_email',
        so          => 'ASC',
    );

    if ( ref $results ne 'HASH' ) {
        return ('mli_recipients.html', %$results);
    }
    return ('mli_recipients.html', $results);
}
END_OF_SUB

$COMPILE{mli_cat_add} = <<'END_OF_SUB';
sub mli_cat_add {
#--------------------------------------------------------------------
# Add a category
#
    my $name = $IN->param('cm_name');
    ( $name ) or return mli_home(GList::language('SYS_ADD_INVALID'));

    my $ret = GList::add('CatMailing', 'cm', { cm_name => $name, cm_type => 2 });
    return mli_home($GList::error) if ($GList::error);

    return mli_home(GList::language('DIR_ADDED', $name)) if ( $ret );
}
END_OF_SUB

$COMPILE{mli_cat_modify} = <<'END_OF_SUB';
sub mli_cat_modify {
#-------------------------------------------------------------------
# Update a category
#
    my $id = $IN->param('cm_id');
    ( $id ) or return mli_home(GList::languag('SYS_ADD_INVALID'));

    GList::modify('CatMailing', 'cm');

    return mli_home($GList::error) if ($GList::error);
    mli_home(GList::language('DIR_UPDATED', $IN->param('cm_name')));
}
END_OF_SUB

$COMPILE{mli_cat_delete} = <<'END_OF_SUB';
sub mli_cat_delete {
#--------------------------------------------------------------------
# Delete a category
#
    my $cgi = $IN->get_hash();

    ( $cgi->{cm_id}) or return mli_home(GList::languag('SYS_ADD_INVALID'));

    $cgi->{modify} = '1';
    $cgi->{'1-cm_id'} = $cgi->{cm_id};
    if ( $USER->{usr_type} != ADMINISTRATOR ) {
        my $owner = $DB->table('CatMailing')->select({ cm_user_id_fk => $USER->{usr_username} }, ['cm_user_id_fk'])->fetchrow_array;
        ( !$owner or $owner ne $USER->{usr_username} ) and return mli_home(GList::language('SYS_PER_DENIED'));
    }

    $DB->table('MailingIndex')->update({ mli_cat_id_fk => 0, mli_delete => '1' }, { mli_cat_id_fk => $cgi->{cm_id} });

    return mli_home(GList::delete('CatMailing', 'cm', $cgi, GList::language('DIR_DELETED', $IN->param('cm_name'))));
}
END_OF_SUB

$COMPILE{mli_schedule} = __LINE__ . <<'END_OF_SUB';
sub mli_schedule {
#--------------------------------------------------------------------
#    
    return mli_home() if $IN->param('bcancel');

    my $mod = ref $IN->param('modify') eq 'ARRAY' ? $IN->param('modify') : [$IN->param('modify')];
    my @ids = map $IN->param("$_-mli_id"), @{$mod};

    require GT::SQL::Condition;
    my $results = $DB->table('MailingIndex')->select(['mli_id', 'mli_subject'], GT::SQL::Condition->new(mli_id => 'IN' => \@ids))->fetchall_hashref;
    return mli_home('0 mailing was scheduled') unless $results;

    return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_SCHEDULE_MSG'), selected_mailings => $results }) unless $IN->param('bschedule');

    my $scm_type = $IN->param('scm_type');
    my $minute   = $IN->param('scm_minute')   || 0;
    my $hour     = $IN->param('scm_hour')     || 0;
    my $text_url = $IN->param('scm_text_url') || '';
    my $html_url = $IN->param('scm_html_url') || '';
    my $option   = '';
    return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_SCHEDULE_MISSING_TYPE'), selected_mailings => $results }) unless $scm_type;
    return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_INVALID_URL'), selected_mailings => $results }) if ($text_url and $text_url !~ /^http/);
    return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_INVALID_URL'), selected_mailings => $results }) if ($html_url and $html_url !~ /^http/);

    if ($scm_type == 1) {
        my $opt_date = $IN->param('opt_date');
        return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_SCHEDULE_MISSING_OPT'), selected_mailings => $results }) unless $opt_date;

        my $format = $USER->{usr_date_format} || '%yyyy%-%mm%-%dd%';
        my $valid  = GList::date_to_time($opt_date, $format);
        return mli_print('mli_schedule_mailing.html', { msg => GList::language('SYS_DATE_FORMAT_INVALID', uc GList::language('SYS_DATE_FORMAT')), selected_mailings => $results }) unless $valid;

        $option = $valid;
    }
    elsif ($scm_type == 3) {
        my $opt_weekly = $IN->param('opt_weekly');
        return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_SCHEDULE_MISSING_OPT'), selected_mailings => $results }) unless $opt_weekly;
        $option = $opt_weekly;
    }
    elsif ($scm_type == 4) {
        my $opt_monthly = $IN->param('opt_monthly');
        return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_SCHEDULE_MISSING_OPT'), selected_mailings => $results }) unless $opt_monthly;
        $option = $opt_monthly;
    }

    my @scheduleds;
    my $db = $DB->table('ScheduledMailings');
    foreach my $m (@$results) {
        $db->insert({ scm_mailing_id_fk => $m->{mli_id}, scm_hour => $hour, scm_minute => $minute, scm_type => $scm_type, scm_option => $option, scm_text_url => $text_url, scm_html_url => $html_url }) or next;
        push @scheduleds, $m->{mli_id};
    }
    $DB->table('MailingIndex')->update({ mli_done => 0, mli_scheduled => 1}, GT::SQL::Condition->new( mli_id => 'IN' => \@scheduleds ));
    mli_home(GList::language('MLI_SCHEDULES_CREATED', $#scheduleds + 1));
}
END_OF_SUB

$COMPILE{mli_schedule_modify} = __LINE__ . <<'END_OF_SUB';
sub mli_schedule_modify {
#--------------------------------------------------------------------
#    
    my $msg    = '';
    my $mli_id = $IN->param('mli_id');
    my $cgi    = $IN->get_hash();
    delete $cgi->{mli_id};

    return mli_home(GList::language('MLI_MISSING_ID')) unless $mli_id;
    return mli_home($msg, $cgi) if $IN->param('bcancel');

    my $schedule = $DB->table('ScheduledMailings', 'MailingIndex')->select(['ScheduledMailings.*', 'MailingIndex.mli_subject'], { scm_mailing_id_fk => $mli_id })->fetchrow_hashref;
    return mli_home("<b>$mli_id</b> not found!") unless $schedule;

    if ($IN->param('mod_action') and $IN->param('mod_action') eq 'delete') {
        return mli_schedule_delete($mli_id);
    }
    elsif ($IN->param('bmodify')) {
        my $scm_type = $IN->param('scm_type');
        my $minute   = $IN->param('scm_minute')   || 0;
        my $hour     = $IN->param('scm_hour')     || 0;
        my $text_url = $IN->param('scm_text_url') || '';
        my $html_url = $IN->param('scm_html_url') || '';
        my $option   = '';

        return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_INVALID_URL'), %$schedule }) if ($text_url and $text_url !~ /^http/);
        return mli_print('mli_schedule_mailing.html', { msg => GList::language('MLI_INVALID_URL'), %$schedule }) if ($html_url and $html_url !~ /^http/);

        if ($scm_type == 1) {
            my $opt_date = $IN->param('opt_date');
            return mli_print('mli_modify_schedule.html', { msg => GList::language('MLI_SCHEDULE_MISSING_OPT'), scm_type => 1, %$schedule }) unless $opt_date;

            my $format = $USER->{usr_date_format} || '%yyyy%-%mm%-%dd%';
            my $valid  = GList::date_to_time($opt_date, $format);
            return mli_print('mli_modify_schedule.html', { msg => GList::language('SYS_DATE_FORMAT_INVALID', uc GList::language('SYS_DATE_FORMAT')), %$schedule }) unless $valid;

            $option = $valid;
        }
        elsif ($scm_type == 3) {
            my $opt_weekly = $IN->param('opt_weekly');
            return mli_print('mli_modify_schedule.html', { msg => GList::language('MLI_SCHEDULE_MISSING_OPT'), scm_type => 3, %$schedule }) unless $opt_weekly;
            $option = $opt_weekly;
        }
        elsif ($scm_type == 4) {
            my $opt_monthly = $IN->param('opt_monthly');
            return mli_print('mli_modify_schedule.html', { msg => GList::language('MLI_SCHEDULE_MISSING_OPT'), scm_type => 4, %$schedule }) unless $opt_monthly;
            $option = $opt_monthly;
        }
        $DB->table('ScheduledMailings')->update({ scm_hour => $hour, scm_minute => $minute, scm_type => $scm_type, scm_inprocess => 0, scm_sent => 0, scm_option => $option, scm_text_url => $text_url, scm_html_url => $html_url }, { scm_mailing_id_fk => $mli_id });
        return mli_home(GList::language('MLI_SCHEDULE_UPDATED', $mli_id), $cgi);
    }

    my ($opt_monthly, $opt_weekly, $opt_date);
    if ($schedule->{scm_type} == 1) {
        require GT::Date;
        my $format = $USER->{usr_date_format} || '%yyyy%-%mm%-%dd%';
        $opt_date = GT::Date::date_get($schedule->{scm_option}, $format);
    }
    elsif ($schedule->{scm_type} == 3) {
        $opt_weekly = $schedule->{scm_option};
    }
    elsif ($schedule->{scm_type} == 4) {
        $opt_monthly = $schedule->{scm_option};
    }
    return mli_print('mli_modify_schedule.html', { msg => $msg, opt_date => $opt_date, opt_weekly => $opt_weekly, opt_monthly => $opt_monthly, %$schedule });
}
END_OF_SUB

$COMPILE{mli_schedule_delete} = __LINE__ . <<'END_OF_SUB';
sub mli_schedule_delete {
#--------------------------------------------------------------------
#    
    my $mli_id = shift;

    my @ids;
    my $cgi = $IN->get_hash();
    delete $cgi->{mli_id};
    if ($mli_id) {
        push @ids, $mli_id;
    }
    else {
        my $mod = ref $IN->param('modify') eq 'ARRAY' ? $IN->param('modify') : [$IN->param('modify')];
        @ids = map $IN->param("$_-mli_id"), @{$mod};
    }
    foreach my $id (@ids) {
        $DB->table('ScheduledMailings')->delete({ scm_mailing_id_fk => $id });
        $DB->table('MailingIndex')->update({ mli_scheduled => 0 }, { mli_id => $id });
    }
    my $msg = $mli_id ? GList::language('MLI_SCHEDULE_DELETED', $mli_id) : GList::language('MLI_SCHEDULES_DELETED', $#ids + 1);
    return mli_home($msg, $cgi);
}
END_OF_SUB

$COMPILE{_print_results} = __LINE__ . <<'END_OF_SUB';
sub _print_results {
    my ($call_from, $checkeds, $bounceds, $deleteds) = @_;
    if ($call_from eq 'web') {
        mli_bounced_form(undef, 'mli_check_bounced_results.html');
    }
    else {
        print qq!\n
    - Total checked email(s): $checkeds
    - Total bounced email(s): $bounceds
    - Total deleted email(s): $deleteds
!;
    }
}
END_OF_SUB

$COMPILE{_bounced} = __LINE__ . <<'END_OF_SUB';
sub _bounced {
#-------------------------------------------------------------------
#
    my ($call_from, $connection, $opts) = @_;

    require GT::Mail::POP3;
    my $pop        = new GT::Mail::POP3 ($connection);
    my $num_emails = $pop->connect;
    if ($GT::Mail::POP3::error) {
        ($call_from eq 'web') ? return mli_bounced_form("<font color=red>$GT::Mail::POP3::error</font>")
                              : die "$GT::Mail::POP3::error";
    }
    if ($call_from eq 'web' and $opts->{save}) { # Save connection to users' profile
        $DB->table('Users')->update({
            usr_mail_host      => $connection->{host},
            usr_mail_port      => $connection->{port} || 110,
            usr_mail_account   => $connection->{user},
            usr_mail_password  => $connection->{pass},
            }, { usr_username => $USER->{usr_username} }
        );
    }
    if ($num_emails == 0) {
        $pop->quit;
        return _print_results($call_from, 0, 0);
    }
    elsif ($call_from eq 'web' and $num_emails > $CFG->{max_bounced_emails}) {
        $pop->quit;
        return mli_bounced_form(GList::language('MLI_OVERLIMIT_BOUNCEDS'));
    }

    my $db_sub = $DB->table('Subscribers');
    my $db_eml = $DB->table('EmailMailings');

# handle the progress bar
    my ($last_width, $checked, $bounced, $deleted) = (0, 0, 0, 0);
    my ($prog_header, $prog_footer) = '';
    my $max_width = ($call_from eq 'web') ? 420 : 50;
    if ($call_from eq 'web') {
        GList::display('mli_progress_bar.html');
        $prog_header = "<script>msg.innerHTML = '".GList::language('MLI_CHECK_BOUNCED')."'</script>";
    }
    else {
        $prog_header = "";
        $prog_footer = "Done";
    }

    print $prog_header;
    foreach ( 1..$num_emails ) {
        $checked++;
        my $content = $pop->retr($_);
        if ($$content =~ /x-glist:\s+(\w+)/i) {
            my $code = $1;
            my $info = $db_eml->get({ eml_code => $code });
            if ($info) {
                $db_sub->update({ sub_bounced => \'sub_bounced + 1' }, { sub_email => $info->{eml_email} });
                $db_eml->update({ eml_bounced => 1 }, { eml_email => $info->{eml_email}, eml_code => $code });
                $bounced++;
                if ($opts->{delete} =~ /1|2/) {
                    if ($pop->dele($_)) {
                        $deleted++;
                    }
                    else {
                        warn "Can't delete email $_: $GT::Mail::POP3::error";
                    }
                }
            }
        }
        elsif ($opts->{delete} == 2) { # delete all option is set.
            if ($pop->dele($_)) {
                $deleted++;
            }
            else {
                warn "Can't delete email $_: $GT::Mail::POP3::error";
            }
        }
        my $wpercent = 1 - ($num_emails - $checked) / $num_emails;
        my $img_width= int($max_width * $wpercent);
        if ($img_width != $last_width) {
            if ($call_from eq 'web') {
                printf "<script>img.width = $img_width; per.innerHTML = '%.f%%'</script>\n", 100 * $wpercent;
            }
            else {
                _print_dot($img_width - $last_width);
            }
            $last_width = $img_width;
        }
    }
    $pop->quit;

    if ($call_from eq 'web') {
        my $url = ($USER->{use_cookie}) ? "$CFG->{cgi_url}/glist.cgi?do=mli_bounced_form;parsed=$num_emails;bounced=$bounced;deleted=$deleted;results=1"
                                        : "$CFG->{cgi_url}/glist.cgi?do=mli_bounced_form;parsed=$num_emails;bounced=$bounced;deleted=$deleted;results=1;sid=$USER->{session_id}";
        print "<script>window.location = '$url'</script>";
    }
    else {
        print $prog_footer;
        _print_results($call_from, $num_emails, $bounced, $deleted);
    }
}
END_OF_SUB

$COMPILE{_send} = __LINE__ . <<'END_OF_SUB';
sub _send {
#---------------------------------------------------------------------
# This subsroutine will be called from either web or shell mode
#
    my ($call_from, $ids, $total_size) = @_;

    require GList::Template;
    require GT::TempFile;

    my $demo = 0;
#------------demo code-----------

    if ($call_from eq 'web') {
        GList::display('mli_progress_bar.html');
        print "<script>msg.innerHTML = '".GList::language('MSG_CHECKING')."'</script>";
    }

    my $start    = time();
    my $started  = scalar localtime;
    my $db_mli   = $DB->table('MailingIndex');
    my $db_eml   = $DB->table('EmailMailings');
    my $db_mat   = $DB->table('MailingAttachments');
    my $db_sub   = $DB->table('Subscribers');
    my $db_usr   = $DB->table('Users');
    my $sub_cols = $DB->table('Subscribers')->cols;
    my $usr_cols = $DB->table('Users')->cols;
    my $num_sent = ($call_from eq 'web') ? (GList::check_limit('email30') || 0) : 0;
    my $data     = $USER || {};
    $data->{cgi_url}   = $CFG->{cgi_url};
    $data->{image_url} = $CFG->{image_url};


# Load StopLists
    my $stoplist = $DB->table('StopLists')->select(['stl_email'])->fetchall_arrayref;
    my %stoplist;
    foreach (@$stoplist) {
        next if (!$_->[0]);
        exists $stoplist{$_->[0]} or $stoplist{$_->[0]} = 1;
    }

    $|++;
    my ($count, $sent_size, $last_width) = (0, 0, -1);
    my ($html_header, $html_footer, $text_header, $text_footer, $prog_header, $prog_footer);
    my $max_width = ($call_from eq 'web') ? 420 : 50;
    my $temp_text = new GT::TempFile;
    my $temp_html = new GT::TempFile;
    if ($call_from eq 'web') {
        $prog_header = "<script>msg.innerHTML = '".GList::language('MSG_SENDING')."'</script>";
        my $url      = ($USER->{use_cookie}) ? "$CFG->{cgi_url}/glist.cgi?do=mli_home;fd=2;sent=$count;demo=$demo"
                                             : "$CFG->{cgi_url}/glist.cgi?do=mli_home;fd=2;sent=$count;sid=$USER->{session_id};demo=$demo";
        $prog_footer = "<script>window.location = '$url'</script>";

# Set header and footer if they are specified
        $html_header = ($CFG->{header_html}) ? "$CFG->{header_html}<BR>$USER->{usr_header_html}" : $USER->{usr_header_html};
        $html_footer = ($CFG->{footer_html}) ? "$USER->{usr_footer_html}<BR>$CFG->{footer_html}" : $USER->{usr_footer_html};
        $text_header = ($CFG->{header_text}) ? "$CFG->{header_text}\n$USER->{usr_header_text}"   : $USER->{usr_header_text};
        $text_footer = ($CFG->{footer_text}) ? "$USER->{usr_footer_text}\n$CFG->{footer_text}"   : $USER->{usr_footer_text};
    }
    else {
        $prog_header = "\nSending messages\n";
        $prog_footer = "Done\n";
    }

    print $prog_header;
    foreach my $mailing (@$ids) {
        my ($msg_size, $att_size, $info) = (0, 0, {});

        if ($call_from eq 'web') {
            last if ($USER->{usr_type} == LIMITED_USER and $num_sent >= $USER->{usr_limit_email30});
            $info = GList::check_owner('MailingIndex', 'mli', $mailing);
        }
        else {
            $info = $db_mli->get($mailing);
            my $user = $db_usr->get({ usr_username => $info->{mli_user_id_fk}});
            if ($user) {
                $data = $user;
                $html_header = ($CFG->{header_html}) ? "$CFG->{header_html}<BR>$user->{usr_header_html}" : $user->{usr_header_html};
                $html_footer = ($CFG->{footer_html}) ? "$user->{usr_footer_html}<BR>$CFG->{footer_html}" : $user->{usr_footer_html};
                $text_header = ($CFG->{header_text}) ? "$CFG->{header_text}\n$user->{usr_header_text}"   : $user->{usr_header_text};
                $text_footer = ($CFG->{footer_text}) ? "$user->{usr_footer_text}\n$CFG->{footer_text}"   : $user->{usr_footer_text};
            }
        }
        next if (!$info or ref $info ne 'HASH');
        next if ($info->{mli_done}); # Skip if it has already been sent

        $count++;
        my $mailings = $db_eml->select({ eml_mailing_id_fk => $mailing, eml_sent => 0 })->fetchall_hashref;
        my $attachs  = $db_mat->select({ mat_mailing_id_fk => $mailing })->fetchall_hashref;

# Figure out the attachments size
        foreach (@$attachs) {
            $att_size += -s "$CFG->{priv_path}/attachments/mailings/" . ($mailing % 10) . "/$mailing/$_->{mat_id}";
        }

# Figure out the message size
        my $content_text = $info->{mli_message_text};
        my $content_html = $info->{mli_message_html};
        $msg_size  = length $content_html if ($content_html);
        $msg_size += length $content_text if ($content_text);

# Add header and footer if they are available
        $content_text  = "$text_header\n$content_text"   if ($content_text and $text_header);
        $content_text .= "\n$text_footer"                if ($content_text and $text_footer);
        $content_html  = "$html_header<BR>$content_html" if ($content_html and $html_header);
        $content_html .= "<BR>$html_footer"              if ($content_html and $html_footer);

        open (TEXT, "> $$temp_text");
        print TEXT $content_text;
        close TEXT;

        if ($info->{mli_message_html}) {
            $content_html =~ s/&lt;%/<%/g;
            $content_html =~ s/%&gt;/%>/g;
            if ($info->{mli_track_open}) { # Insert track openning code
                $content_html.= ($CFG->{iframe_tracking}) ? TRACK_OPEN_HTML : TRACK_OPEN_HTML_NOIFRAME;
            }
            $content_html = _replace_url($content_html, TRACK_CLICK_URL) if ($info->{mli_track_click});
            open (HTML, "> $$temp_html");
            print HTML $content_html;
            close HTML;
        }
        foreach my $m (@$mailings) {
            last if ($call_from eq 'web' and $USER->{usr_type} == LIMITED_USER and $num_sent >= $USER->{usr_limit_email30});
            next unless $db_eml->count( eml_id => $m->{eml_id}, eml_sent => '0' );

            if ( exists $stoplist{$m->{eml_email}} ) { # skip email if it's in stoplist
                $db_eml->update({ eml_skipped => '1', eml_sent => time }, { eml_id => $m->{eml_id} });
                next;
            }

            my $bounce_code  = _generate_bounce_code();
            my $sth = $db_eml->update({ eml_sent => time, eml_code => $bounce_code }, { eml_id => $m->{eml_id}, eml_sent => 0 })
                or next;
            my $rows = $sth->rows;
            next unless $rows;

# Allows personalizing of messages using <%...%> tags
            my $lists        = join ';', map "lid=$_", split ',', $m->{eml_lists};
            $data->{mailing} = $info->{mli_id};
            $data->{eml_code}= $bounce_code;
            $data->{unsubscribe_url} = ($info->{mli_track_click}) ? "$CFG->{cgi_url}/glist.cgi?do=user_click;mailing=$info->{mli_id};url=".$IN->escape("$CFG->{cgi_url}/glist.cgi?do=user_rm;eml_code=$bounce_code;$lists")
                                                                  : "$CFG->{cgi_url}/glist.cgi?do=user_rm;eml_code=$bounce_code;$lists";

            #--------------------------
            # LJM: Parse out arbitrary lists - keys will replace these
            #--------------------------
            $lists = "lid=";
            $data->{unsubscribe_list} = ($info->{mli_track_click}) ? "$CFG->{cgi_url}/glist.cgi?do=user_click;mailing=$info->{mli_id};url=".$IN->escape("$CFG->{cgi_url}/glist.cgi?do=user_rm;eml_code=$bounce_code;$lists")
                                                                   : "$CFG->{cgi_url}/glist.cgi?do=user_rm;eml_code=$bounce_code;$lists";
            $data->{subscribe_list} = ($info->{mli_track_click}) ? "$CFG->{cgi_url}/glist.cgi?do=user_click;mailing=$info->{mli_id};url=".$IN->escape("$CFG->{cgi_url}/glist.cgi?do=user_subscribe;eml_code=$bounce_code;$lists")
                                                                    : "$CFG->{cgi_url}/glist.cgi?do=user_subscribe;eml_code=$bounce_code;$lists";
            $lists = "from_to_lid=";
            $data->{move_list} = ($info->{mli_track_click}) ? "$CFG->{cgi_url}/glist.cgi?do=user_click;mailing=$info->{mli_id};url=".$IN->escape("$CFG->{cgi_url}/glist.cgi?do=user_move;eml_code=$bounce_code;$lists")
                                                                   : "$CFG->{cgi_url}/glist.cgi?do=user_move;eml_code=$bounce_code;$lists";
            #--------------------------

            foreach ( keys %$sub_cols ) { # Subscriber's information
                (my $c = $_) =~ s/sub/eml/;
                $data->{$_} = $m->{$c};
            }

            my $text = $content_text;
            my $html = $content_html;
            my $key  = join '|', map quotemeta, keys %$data;

            $text =~ s/<%($key)%>/$data->{$1}/g;
            $html =~ s/<%($key)%>/$data->{$1}/g;

            $text = GList::Template->parse($$temp_text, $data, { disable => { functions => 1 } }) if ($text =~ /<%/);
            $html = GList::Template->parse($$temp_html, $data, { disable => { functions => 1 } }) if ($html and $html =~ /<%/);

            my %head;
            my $to_quoted   = "$m->{eml_name} ";
            my $from_quoted = "$info->{mli_name} ";
            if ($to_quoted =~ /[^\w\s]/) {
                $to_quoted =~ s/([\\"])/\\$1/g;
                $to_quoted = '"' . substr($to_quoted, 0, -1) . '" ';
            }
            if ($from_quoted =~ /[^\w\s]/) {
                $from_quoted =~ s/([\\"])/\\$1/g;
                $from_quoted = '"' . substr($from_quoted, 0, -1) . '" ';
            }
            $head{from}         = $info->{mli_name} ? $from_quoted . "<$info->{mli_from}>" : $info->{mli_from};
            $head{to}           = $m->{eml_name} ? $to_quoted . "<$m->{eml_email}>" : $m->{eml_email};
            $head{subject}      = $info->{mli_subject};
            $head{'Reply-To'}   = $info->{mli_reply_to};
            $head{'Return-Path'}= $info->{mli_bounce_email};
            $head{'X-GList'}    = $bounce_code;

# Handle the progress bar
            $sent_size   += $msg_size + $att_size;
            my $wpercent  = 1 - ($total_size - $sent_size) / $total_size;
            my $img_width = int($max_width * $wpercent);

            if (!$demo) {
                GList::send(\%head, { text => $text, html => $html }, $attachs, "$CFG->{priv_path}/attachments/mailings/" . ($mailing % 10) . "/$mailing", $info->{mli_charset});
            }
            $num_sent++;

            if ( $img_width != $last_width ) {
                if ($call_from eq 'web') {
                    printf "<script>img.width = $img_width; per.innerHTML = '%.f%%'</script>\n", 100 * $wpercent;
                }
                else {
                    _print_dot($img_width - $last_width);
                }
                $last_width = $img_width;
            }
        }
        if (!$db_eml->count({ eml_mailing_id_fk => $mailing, eml_sent => 0 })) {
            $db_mli->update({ mli_done => time, mli_cat_id_fk => 0 }, { mli_id => $mailing });
        }
    }
    print $prog_footer;
}
END_OF_SUB

$COMPILE{_size_mailings} = __LINE__ . <<'END_OF_SUB';
sub _size_mailings {
#--------------------------------------------------------------------
# Get the size of mailings
#
    my ($ids, $call_from) = @_;

    my $db_attach = $DB->table('MailingAttachments');
    my $db_email  = $DB->table('EmailMailings');
    my $db_mailing= $DB->table('MailingIndex');
    my $size      = 0;
    foreach my $mailing ( @$ids ) {
        my $length = 0;
        my $info;

# Check who owns it
        if ($call_from eq 'web') {
            $info = GList::check_owner('MailingIndex', 'mli', $mailing);
            next if (ref $info ne 'HASH');
        }
        else {
            $info = $db_mailing->get($mailing);
            next if (!$info);
        }

# Skip if it's been completed
        next if ( $info->{mli_done} );

# Of Text and HTML message
        $length += length $info->{mli_message_text} if ($info->{mli_message_text});
        $length += length $info->{mli_message_html} if ($info->{mli_message_html});

# Get the size of attachments
        my $attach = $db_attach->select({ mat_mailing_id_fk => $mailing });

        while ( my $rs = $attach->fetchrow_hashref ) {
            $length += -s "$CFG->{priv_path}/attachments/mailings/" . ($mailing % 10) . "/$mailing/$rs->{mat_id}";
        }

        my $emails = $db_email->count({ eml_mailing_id_fk => $mailing, eml_sent => '0' });
        $length *= $emails if ( $emails );
        $size   += $length;
    }
    return $size;
}
END_OF_SUB

$COMPILE{_load_navigator} = __LINE__ . <<'END_OF_SUB';
sub _load_navigator {
#---------------------------------------------------------------------
# Generates Category listings
#
    my $user = GList::load_condition();

    my $db   = $DB->table('CatMailing', 'MailingIndex');
    my $cond = GT::SQL::Condition->new('cm_user_id_fk', $user->{opt}, $user->{id});
    $db->select_options('GROUP BY cm_type,cm_id, cm_name ORDER BY cm_name');

    my $sth = $db->select('left_join', $cond, ['CatMailing.cm_id', 'CatMailing.cm_type', 'CatMailing.cm_name', 'count(mli_id) as mailing']) or die "$GT::SQL::error";
    my ($draft, $sent);
    while ( my $rs = $sth->fetchrow_hashref ) {
        if ( $rs->{cm_type} eq '1' ) {
            push @$draft, $rs;
        }
        else {
            push @$sent, $rs;
        }
    }

    my $db_mli = $DB->table('MailingIndex');
    my $cd     = GT::SQL::Condition->new(
        mli_user_id_fk => $user->{opt} => $user->{id},
        mli_delete     => '=' => 0,
        mli_done       => '=' => 0,
        mli_scheduled  => '=' => 0,
        mli_cat_id_fk  => '=' => 0,
    );
    my $drafts    = $db_mli->select($cd)->rows;
    my $scheduled = $db_mli->select({ mli_scheduled => 1, mli_delete => 0 })->rows;

    return { results_draft  => $draft, results_sent     => $sent, scheduled_hits => $scheduled,
             hits_draft     => $#$draft + 1, hits_sent  => $#$sent + 1, drafts => $drafts, 
    };
}
END_OF_SUB

$COMPILE{_generate_bounce_code} = __LINE__ . <<'END_OF_SUB';
sub _generate_bounce_code {
# -------------------------------------------------------------------
    my $code;
    my $i;
    while ($i++ < 10) {
        $code  = '';
        my @chars = ('a' .. 'z', 'A' .. 'Z', 0 .. 9);
        for (1 .. 20) {
            $code .= $chars[rand @chars];
        }
        last unless ($DB->table('EmailMailings')->count( { eml_code => $code } ));
    }
    return $code;
}
END_OF_SUB

$COMPILE{_print_dot} = __LINE__ . <<'END_OF_SUB';
sub _print_dot {
    my $num = shift;
    foreach my $i(1..$num) {
        print ".";
    }
}
END_OF_SUB

$COMPILE{_replace_url} = __LINE__ . <<'END_OF_SUB';
sub _replace_url {
    my ($content, $url) = @_;
    $url ||= '';
    $content =~ s/href\s*=\s*(["'])\s*((?:https?|ftp):\/\/.*?)\1/my $link = $IN->escape($IN->html_unescape($2)); "href=$1$url;url=$link$1"/gise;
    return $content;
}
END_OF_SUB

$COMPILE{_determine_action} = __LINE__ . <<'END_OF_SUB';
sub _determine_action {
#----------------------------------------------------------------------------
# Check valid action
#
    my $action = shift || undef;
    return            if ( !$action );
    return 'mli_home' if ( $action eq 'mli_search' );

    my %valid = (
        map { $_ => 1 } qw(
            mli_home
            mli_search_form
            mli_empty
            mli_delete
            mli_move
            mli_schedule
            mli_schedule_modify
            mli_schedule_delete
            mli_send
            mli_fview
            mli_fdownload
            mli_bounced_form
            mli_bounced
            mli_recipients
            mli_cat_add
            mli_cat_modify
            mli_cat_delete
        )
    );
    exists $valid{$action} and return $action;
    return;
}
END_OF_SUB
1;
