#!/usr/bin/perl
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

use 5.10.1;
use strict;
use warnings;

use FindBin '$RealBin';
use lib "$RealBin/../..", "$RealBin/../../lib";

use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Field;
use Bugzilla::User;

use Pod::Usage;

# Load extensions for monkeypatched $user->clear_last_statistics_ts()
BEGIN { Bugzilla->extensions(); }

Bugzilla->usage_mode(USAGE_MODE_CMDLINE);

if (scalar @ARGV < 1) {
    die <<USAGE;
Usage: security_remove.pl <user>

E.g.: security_remove.pl foo\@bar.com

will remove the specified user from the following roles on private only bugs:

- Set reporter_accessible to false if user is the reporter
- Remove from the cc list
- Clear qa_contact if the user is qa_contact
- Re-assign to nobody\@mozilla.org if user is the assignee

This script should not touch user's membership or default assignee, qa contact,
or cc settings for components.

Script should not send any email about the changes.
USAGE
}

my ($login_name) = @ARGV;

# Load nobody user and set as current
my $auto_user = Bugzilla::User->check({ name => 'automation@bmo.tld' });
Bugzilla->set_user($auto_user);

# Check target user
my $target_user = Bugzilla::User->check({ name => $login_name });

my $dbh = Bugzilla->dbh;
my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');

# Gather bug ids
my $reporter_bugs = $dbh->selectcol_arrayref(
    q{SELECT DISTINCT bugs.bug_id
        FROM bugs, bug_group_map
       WHERE bugs.bug_id = bug_group_map.bug_id
             AND bugs.reporter_accessible = 1
             AND bugs.reporter = ?},
    undef, $target_user->id) || [];

my $assignee_bugs = $dbh->selectcol_arrayref(
    q{SELECT DISTINCT bugs.bug_id
        FROM bugs, bug_group_map
       WHERE bugs.bug_id = bug_group_map.bug_id
             AND bugs.assigned_to = ?},
    undef, $target_user->id) || [];

my $qa_bugs = $dbh->selectcol_arrayref(
    q{SELECT DISTINCT bugs.bug_id
        FROM bugs, bug_group_map
       WHERE bugs.bug_id = bug_group_map.bug_id
             AND bugs.qa_contact = ?},
    undef, $target_user->id) || [];

my $cc_bugs = $dbh->selectcol_arrayref(
    q{SELECT DISTINCT cc.bug_id
        FROM cc, bug_group_map
       WHERE cc.bug_id = bug_group_map.bug_id
             AND cc.who = ?},
    undef, $target_user->id) || [];

my $reporter_count = scalar @$reporter_bugs;
my $assignee_count = scalar @$assignee_bugs;
my $qa_count       = scalar @$qa_bugs;
my $cc_count       = scalar @$cc_bugs;

if (!$reporter_count
    && !$assignee_count
    && !$qa_count
    && !$cc_count)
{
    warn "There are no bugs to update.\n";
    exit 1;
}

warn <<EOF;
About to remove user from the following number of bugs:

Reporter:   $reporter_count
Assignee:   $assignee_count
QA Contact: $qa_count
CC:         $cc_count

Press <Ctrl-C> to stop or <Enter> to continue...
EOF
getc();

$dbh->bz_start_transaction;

# Reporter - set reporter_accessible to false
my $field_id = get_field_id('reporter_accessible');
foreach my $bug_id (@$reporter_bugs) {
    warn "Updating bug $bug_id\n";
    $dbh->do(
        q{INSERT INTO bugs_activity (bug_id, who, bug_when, fieldid, removed, added)
         VALUES (?, ?, ?, ?, ?, ?)},
        undef, $bug_id, $auto_user->id, $timestamp, $field_id, 1, 0);
    $dbh->do(
        q{UPDATE bugs SET reporter_accessible = 0, delta_ts = ?, lastdiffed = ?
           WHERE bug_id = ?},
        undef, $timestamp, $timestamp, $bug_id);
}

# Assignee
$field_id = get_field_id('assigned_to');
foreach my $bug_id (@$assignee_bugs) {
    warn "Updating bug $bug_id\n";
    $dbh->do(
        q{INSERT INTO bugs_activity (bug_id, who, bug_when, fieldid, removed, added)
          VALUES (?, ?, ?, ?, ?, ?)},
        undef, $bug_id, $auto_user->id, $timestamp, $field_id,
               $target_user->login, $auto_user->login);
    $dbh->do(
        q{UPDATE bugs SET assigned_to = ?, delta_ts = ?, lastdiffed = ?
           WHERE bug_id = ?},
        undef, $auto_user->id, $timestamp, $timestamp, $bug_id);
}

# QA Contact
$field_id = get_field_id('qa_contact');
foreach my $bug_id (@$qa_bugs) {
    warn "Updating bug $bug_id\n";
    $dbh->do(
        q{INSERT INTO bugs_activity (bug_id, who, bug_when, fieldid, removed, added)
          VALUES (?, ?, ?, ?, ?, '')},
        undef, $bug_id, $auto_user->id, $timestamp, $field_id, $target_user->login);
    $dbh->do(
        q{UPDATE bugs SET qa_contact = NULL, delta_ts = ?, lastdiffed = ?
           WHERE bug_id = ?},
        undef, $timestamp, $timestamp, $bug_id);
}

# CC list
$field_id = get_field_id('cc');
foreach my $bug_id (@$cc_bugs) {
    warn "Updating bug $bug_id\n";
    $dbh->do(
        q{INSERT INTO bugs_activity (bug_id, who, bug_when, fieldid, removed, added)
          VALUES (?, ?, ?, ?, ?, '')},
        undef, $bug_id, $auto_user->id, $timestamp, $field_id, $target_user->login);
    $dbh->do(q{DELETE FROM cc WHERE bug_id = ? AND who = ?},
             undef, $bug_id, $target_user->id);
}

$target_user->clear_last_statistics_ts();

$dbh->bz_commit_transaction;

# It's complex to determine which items now need to be flushed from memcached.
# As this is expected to be a rare event, we just flush the entire cache.
Bugzilla->memcached->clear_all();

__END__

=head1 NAME

security_remove.pl - Remove user from any role associated with private bugs.

=head1 SYNOPSIS

    security_remove.pl foo@bar.com