#!/usr/bin/perl -wT
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Erik Stambaugh <erik@dasbistro.com>
#

use strict;

use lib qw(. lib);

use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::User;
use Bugzilla::Group;
use Bugzilla::Token;
use Bugzilla::Whine::Schedule;
use Bugzilla::Whine::Query;

# require the user to have logged in
my $user = Bugzilla->login(LOGIN_REQUIRED);

my $ARGS = Bugzilla->input_params;
my $template = Bugzilla->template;
my $vars = {};
my $dbh = Bugzilla->dbh;

my $userid = $user->id;
my $token = $ARGS->{token};

# $events is a hash ref, keyed by event id, that stores the active user's
# events.  It starts off with:
#  'subject' - the subject line for the email message
#  'body'    - the text to be sent at the top of the message
#
# Eventually, it winds up with:
#  'queries'  - array ref containing hashes of:
#       'name'          - the name of the saved query
#       'title'         - The title line for the search results table
#       'sort'          - Numeric sort ID
#       'id'            - row ID for the query entry
#       'onemailperbug' - whether a single message must be sent for each
#                         result.
#  'schedule' - array ref containing hashes of:
#       'day' - Day or range of days this schedule will be run
#       'time' - time or interval to run
#       'mailto_type' - MAILTO_USER or MAILTO_GROUP
#       'mailto' - person/group who will receive the results
#       'id' - row ID for the schedule
my $events = get_events($userid);

# First see if this user may use whines
$user->in_group('bz_canusewhines') || ThrowUserError("auth_failure", {
    group  => "bz_canusewhines",
    action => "schedule",
    object => "reports",
});

# May this user send mail to other users?
my $can_mail_others = Bugzilla->user->in_group('bz_canusewhineatothers');

# If the form was submitted, we need to look for what needs to be added or
# removed, then what was altered.
if ($ARGS->{update})
{
    check_token_data($token, 'edit_whine');

    if ($ARGS->{add_event})
    {
        # we create a new event
        $dbh->do("INSERT INTO whine_events (owner_userid) VALUES (?)", undef, $userid);
    }
    else
    {
        for my $eventid (keys %{$events})
        {
            # delete an entire event
            if ($ARGS->{"remove_event_$eventid"})
            {
                # We need to make sure these belong to the same user,
                # otherwise we could simply delete whatever matched that ID.
                $dbh->do("DELETE FROM whine_events WHERE id=? AND owner_userid=?", undef, $eventid, $userid);
            }
            else
            {
                # check the subject, body and mailifnobugs for changes
                my $subject = $ARGS->{"event_${eventid}_subject"} || '';
                my $body    = $ARGS->{"event_${eventid}_body"} || '';
                my $mailifnobugs = $ARGS->{"event_${eventid}_mailifnobugs"} ? 1 : 0;

                trick_taint($subject) if $subject;
                trick_taint($body)    if $body;

                if ($subject ne $events->{$eventid}->{subject} ||
                    $mailifnobugs != $events->{$eventid}->{mailifnobugs} ||
                    $body ne $events->{$eventid}->{body})
                {
                    $dbh->do(
                        "UPDATE whine_events SET subject=?, body=?, mailifnobugs=? WHERE id=?",
                        undef, $subject, $body, $mailifnobugs, $eventid
                    );
                }

                # add a schedule
                if ($ARGS->{"add_schedule_$eventid"})
                {
                    # the schedule table must be locked before altering
                    $dbh->do(
                        "INSERT INTO whine_schedules (eventid, mailto_type, mailto, " .
                        "run_day, run_time) VALUES (?, ?, ?, 'Sun', 2)",
                        undef, $eventid, MAILTO_USER, $userid
                    );
                }
                # add a query
                elsif ($ARGS->{"add_query_$eventid"})
                {
                    $dbh->do("INSERT INTO whine_queries (eventid) VALUES (?)", undef, $eventid);
                }
            }

            # now check all of the schedules and queries to see if they need
            # to be altered or deleted

            # Check schedules for changes
            my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $eventid });
            my @scheduleids = ();
            foreach my $schedule (@$schedules)
            {
                push @scheduleids, $schedule->id;
            }

            # we need to double-check all of the user IDs in mailto to make
            # sure they exist
            my $arglist = {};   # args for match_field
            for my $sid (@scheduleids)
            {
                if ($ARGS->{"mailto_type_$sid"} == MAILTO_USER)
                {
                    $arglist->{"mailto_$sid"} = { type => 'single' };
                }
            }
            if (scalar %{$arglist})
            {
                Bugzilla::User::match_field($arglist);
            }

            for my $sid (@scheduleids)
            {
                if ($ARGS->{"remove_schedule_$sid"})
                {
                    # having the assignee id in here is a security failsafe
                    $dbh->do(
                        "DELETE FROM whine_schedules WHERE id=?".
                        " AND (SELECT owner_userid FROM whine_events WHERE whine_events.id=eventid)=?",
                        undef, $sid, $userid
                    );
                }
                else
                {
                    my $o_day         = $ARGS->{"orig_day_$sid"} || '';
                    my $day           = $ARGS->{"day_$sid"} || '';
                    my $o_time        = $ARGS->{"orig_time_$sid"} || 0;
                    my $time          = $ARGS->{"time_$sid"} || 0;
                    my $o_mailto      = $ARGS->{"orig_mailto_$sid"} || '';
                    my $mailto        = $ARGS->{"mailto_$sid"} || '';
                    my $o_mailto_type = $ARGS->{"orig_mailto_type_$sid"} || 0;
                    my $mailto_type   = $ARGS->{"mailto_type_$sid"} || 0;

                    my $mailto_id = $userid;

                    # get an id for the mailto address
                    if ($can_mail_others && $mailto)
                    {
                        if ($mailto_type == MAILTO_USER)
                        {
                            $mailto_id = login_to_id($mailto);
                        }
                        elsif ($mailto_type == MAILTO_GROUP)
                        {
                            # The group name is used in a placeholder.
                            trick_taint($mailto);
                            $mailto_id = Bugzilla::Group::ValidateGroupName($mailto, ($user))
                                || ThrowUserError('invalid_group_name', { name => $mailto });
                        }
                        else
                        {
                            # bad value, so it will just mail to the whine
                            # owner.  $mailto_id was already set above.
                            $mailto_type = MAILTO_USER;
                        }
                    }

                    detaint_natural($mailto_type);

                    if ($o_day ne $day || $o_time ne $time ||
                        $o_mailto ne $mailto || $o_mailto_type != $mailto_type)
                    {
                        trick_taint($day);
                        trick_taint($time);

                        # the schedule table must be locked
                        $dbh->do(
                            "UPDATE whine_schedules SET run_day=?, run_time=?,".
                            " mailto_type=?, mailto=?, run_next=NULL WHERE id=?",
                            undef, $day, $time, $mailto_type, $mailto_id, $sid
                        );
                    }
                }
            }

            # Check queries for changes
            my $queries = Bugzilla::Whine::Query->match({ eventid => $eventid });
            for my $query (@$queries)
            {
                my $qid = $query->id;
                if ($ARGS->{"remove_query_$qid"})
                {
                    $dbh->do(
                        "DELETE FROM whine_queries WHERE id=?".
                        " AND (SELECT owner_userid FROM whine_events WHERE whine_events.id=eventid)=?",
                        undef, $qid, $userid
                    );
                }
                else
                {
                    my $o_sort      = $ARGS->{"orig_query_sort_$qid"} || 0;
                    my $sort        = $ARGS->{"query_sort_$qid"} || 0;
                    my $o_queryname = $ARGS->{"orig_query_name_$qid"} || '';
                    my $queryname   = $ARGS->{"query_name_$qid"} || '';
                    my $o_title     = $ARGS->{"orig_query_title_$qid"} || '';
                    my $title       = $ARGS->{"query_title_$qid"} || '';
                    my $o_onemailperbug = $ARGS->{"orig_query_onemailperbug_$qid"} || 0;
                    my $onemailperbug   = $ARGS->{"query_onemailperbug_$qid"} ? 1 : 0;

                    if ($o_sort != $sort || $o_queryname ne $queryname ||
                        $o_onemailperbug != $onemailperbug || $o_title ne $title)
                    {
                        detaint_natural($sort);
                        trick_taint($queryname);
                        trick_taint($title);
                        $dbh->do(
                            "UPDATE whine_queries SET sortkey=?, query_name=?, title=?, onemailperbug=? WHERE id=?",
                            undef, $sort, $queryname, $title, $onemailperbug, $qid
                        );
                    }
                }
            }
        }
    }
    delete_token($token);
}

$vars->{mail_others} = $can_mail_others;

# Get events again, to cover any updates that were made
$events = get_events($userid);

# Here is the data layout as sent to the template:
#
#   events
#       event_id #
#           schedule
#               day
#               time
#               mailto
#           queries
#               name
#               title
#               sort
#
# build the whine list by event id
for my $event_id (keys %{$events})
{
    $events->{$event_id}->{schedule} = [];
    $events->{$event_id}->{queries} = [];

    # schedules
    my $schedules = Bugzilla::Whine::Schedule->match({ eventid => $event_id });
    foreach my $schedule (@$schedules)
    {
        my $mailto_type = $schedule->mailto_is_group ? MAILTO_GROUP : MAILTO_USER;
        my $mailto = '';
        if ($mailto_type == MAILTO_USER)
        {
            $mailto = $schedule->mailto->login;
        }
        elsif ($mailto_type == MAILTO_GROUP)
        {
            $mailto = $schedule->mailto->name;
        }
        push @{$events->{$event_id}->{schedule}}, {
            day         => $schedule->run_day,
            time        => $schedule->run_time,
            mailto_type => $mailto_type,
            mailto      => $mailto,
            id          => $schedule->id,
        };
    }

    # queries
    my $queries = Bugzilla::Whine::Query->match({ eventid => $event_id });
    for my $query (@$queries)
    {
        push @{$events->{$event_id}->{queries}}, {
            name          => $query->name,
            title         => $query->title,
            sort          => $query->sortkey,
            id            => $query->id,
            onemailperbug => $query->one_email_per_bug,
        };
    }
}

$vars->{events} = $events;

# get the available queries
$vars->{available_queries} = $dbh->selectcol_arrayref("SELECT name FROM namedqueries WHERE userid=?", undef, $userid) || [];
$vars->{token} = issue_session_token('edit_whine');
$vars->{local_timezone} = Bugzilla->local_timezone->short_name_for_datetime(DateTime->now());

$template->process("whine/schedule.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
exit;

# get_events takes a userid and returns a hash, keyed by event ID, containing
# the subject and body of each event that user owns
sub get_events
{
    my $userid = shift;
    return Bugzilla->dbh->selectall_hashref(
        "SELECT DISTINCT id, subject, body, mailifnobugs FROM whine_events WHERE owner_userid=?",
        'id', undef, $userid
    );
}
