post_bug.cgi 13.9 KB
Newer Older
1
#!/usr/bin/perl -wT
2
# -*- Mode: perl; indent-tabs-mode: nil -*-
terry%netscape.com's avatar
terry%netscape.com committed
3
#
4 5 6 7 8 9 10 11 12 13
# 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.
#
terry%netscape.com's avatar
terry%netscape.com committed
14
# The Original Code is the Bugzilla Bug Tracking System.
15
#
terry%netscape.com's avatar
terry%netscape.com committed
16
# The Initial Developer of the Original Code is Netscape Communications
17 18 19 20
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
terry%netscape.com's avatar
terry%netscape.com committed
21
# Contributor(s): Terry Weissman <terry@mozilla.org>
22
#                 Dan Mosedale <dmose@mozilla.org>
23
#                 Joe Robins <jmrobins@tgix.com>
24
#                 Gervase Markham <gerv@gerv.net>
25
#                 Marc Schumann <wurblzap@gmail.com>
terry%netscape.com's avatar
terry%netscape.com committed
26

27
use strict;
28 29
use lib qw(.);

30
use Bugzilla;
31
use Bugzilla::Attachment;
32
use Bugzilla::Constants;
33
use Bugzilla::Util;
34
use Bugzilla::Error;
35
use Bugzilla::Bug;
36
use Bugzilla::User;
37
use Bugzilla::Field;
38
use Bugzilla::Product;
39
use Bugzilla::Component;
40
use Bugzilla::Keyword;
41
use Bugzilla::Token;
42
use Bugzilla::Flag;
43

44
my $user = Bugzilla->login(LOGIN_REQUIRED);
45

46
my $cgi = Bugzilla->cgi;
47
my $dbh = Bugzilla->dbh;
48 49
my $template = Bugzilla->template;
my $vars = {};
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
######################################################################
# Subroutines
######################################################################

# Determines whether or not a group is active by checking
# the "isactive" column for the group in the "groups" table.
# Note: This function selects groups by id rather than by name.
sub GroupIsActive {
    my ($group_id) = @_;
    $group_id ||= 0;
    detaint_natural($group_id);
    my ($is_active) = Bugzilla->dbh->selectrow_array(
        "SELECT isactive FROM groups WHERE id = ?", undef, $group_id);
    return $is_active;
}

######################################################################
# Main Script
######################################################################

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
# Detect if the user already used the same form to submit a bug
my $token = trim($cgi->param('token'));
if ($token) {
    my ($creator_id, $date, $old_bug_id) = Bugzilla::Token::GetTokenData($token);
    unless ($creator_id
              && ($creator_id == $user->id)
              && ($old_bug_id =~ "^createbug:"))
    {
        # The token is invalid.
        ThrowUserError('token_inexistent');
    }

    $old_bug_id =~ s/^createbug://;

    if ($old_bug_id && (!$cgi->param('ignore_token')
                        || ($cgi->param('ignore_token') != $old_bug_id)))
    {
        $vars->{'bugid'} = $old_bug_id;
        $vars->{'allow_override'} = defined $cgi->param('ignore_token') ? 0 : 1;

        print $cgi->header();
        $template->process("bug/create/confirm-create-dupe.html.tmpl", $vars)
           || ThrowTemplateError($template->error());
        exit;
    }
}    

98 99
# do a match on the fields if applicable

100
&Bugzilla::User::match_field ($cgi, {
101 102
    'cc'            => { 'type' => 'multi'  },
    'assigned_to'   => { 'type' => 'single' },
103
    'qa_contact'    => { 'type' => 'single' },
104
    '^requestee_type-(\d+)$' => { 'type' => 'multi' },
105
});
106 107 108 109 110

# The format of the initial comment can be structured by adding fields to the
# enter_bug template and then referencing them in the comment template.
my $comment;

111 112
my $format = $template->get_format("bug/create/comment",
                                   scalar($cgi->param('format')), "txt");
113

114
$template->process($format->{'template'}, $vars, \$comment)
115 116
  || ThrowTemplateError($template->error());

117
# Check that the product exists and that the user
118
# is allowed to enter bugs into this product.
119
my $product = Bugzilla::Bug->_check_product($cgi->param('product'));
dmose%mozilla.org's avatar
dmose%mozilla.org committed
120

121
# Set cookies
122 123
if (defined $cgi->param('product')) {
    if (defined $cgi->param('version')) {
124
        $cgi->send_cookie(-name => "VERSION-" . $product->name,
125 126
                          -value => $cgi->param('version'),
                          -expires => "Fri, 01-Jan-2038 00:00:00 GMT");
127 128
    }
}
terry%netscape.com's avatar
terry%netscape.com committed
129

130
if (defined $cgi->param('maketemplate')) {
131
    $vars->{'url'} = $cgi->query_string();
132
    $vars->{'short_desc'} = $cgi->param('short_desc');
terry%netscape.com's avatar
terry%netscape.com committed
133
    
134
    print $cgi->header();
135 136
    $template->process("bug/create/make-template.html.tmpl", $vars)
      || ThrowTemplateError($template->error());
137
    exit;
terry%netscape.com's avatar
terry%netscape.com committed
138 139
}

140
umask 0;
terry%netscape.com's avatar
terry%netscape.com committed
141

142
# This has to go somewhere after 'maketemplate' 
143
# or it breaks bookmarks with no comments. 
144
$comment = Bugzilla::Bug->_check_comment($cgi->param('comment'));
145 146 147
# If comment is all whitespace, it'll be null at this point. That's
# OK except for the fact that it causes e-mail to be suppressed.
$comment = $comment ? $comment : " ";
148

149 150
my $cc_ids = Bugzilla::Bug->_check_cc([$cgi->param('cc')]);
my @keyword_ids = @{Bugzilla::Bug->_check_keywords($cgi->param('keywords'))};
151

152 153
# XXX These checks are only here until strict_isolation can move fully
# into Bugzilla::Bug.
154
my $component = Bugzilla::Bug->_check_component($product, 
155
    $cgi->param('component'));
156
my $assigned_to_id = Bugzilla::Bug->_check_assigned_to($component,
157
    $cgi->param('assigned_to'));
158
my $qa_contact_id = Bugzilla::Bug->_check_qa_contact($component,
159
    $cgi->param('qa_contact'));
160
Bugzilla::Bug->_check_strict_isolation($product, $cc_ids, $assigned_to_id, 
161
    $qa_contact_id);
162

163
my ($depends_on_ids, $blocks_ids) = Bugzilla::Bug->_check_dependencies(
164
    scalar $cgi->param('dependson'), scalar $cgi->param('blocked'));
165

166
# get current time
167
my $timestamp = $dbh->selectrow_array(q{SELECT NOW()});
168

169
# Groups
170
my @groupstoadd = ();
171 172 173 174 175
my $sth_othercontrol = $dbh->prepare(q{SELECT othercontrol
                                         FROM group_control_map
                                        WHERE group_id = ?
                                          AND product_id = ?});

176 177
foreach my $b (grep(/^bit-\d*$/, $cgi->param())) {
    if ($cgi->param($b)) {
178
        my $v = substr($b, 4);
179
        detaint_natural($v)
180
          || ThrowUserError("invalid_group_ID");
181 182 183 184 185
        if (!GroupIsActive($v)) {
            # Prevent the user from adding the bug to an inactive group.
            # Should only happen if there is a bug in Bugzilla or the user
            # hacked the "enter bug" form since otherwise the UI 
            # for adding the bug to the group won't appear on that form.
186
            $vars->{'bit'} = $v;
187
            ThrowCodeError("inactive_group");
188
        }
189
        my ($permit) = $user->in_group_id($v);
190
        if (!$permit) {
191
            my $othercontrol = $dbh->selectrow_array($sth_othercontrol, 
192
                                                     undef, ($v, $product->id));
193 194 195 196
            $permit = (($othercontrol == CONTROLMAPSHOWN)
                       || ($othercontrol == CONTROLMAPDEFAULT));
        }
        if ($permit) {
197 198
            push(@groupstoadd, $v)
        }
199 200 201
    }
}

202 203 204 205 206 207 208 209 210
my $groups = $dbh->selectall_arrayref(q{
                 SELECT DISTINCT groups.id, groups.name, membercontrol,
                                 othercontrol, description
                            FROM groups
                       LEFT JOIN group_control_map 
                              ON group_id = id
                             AND product_id = ?
                           WHERE isbuggroup != 0
                             AND isactive != 0
211
                        ORDER BY description}, undef, $product->id);
212 213 214

foreach my $group (@$groups) {
    my ($id, $groupname, $membercontrol, $othercontrol) = @$group;
215 216 217 218 219
    $membercontrol ||= 0;
    $othercontrol ||= 0;
    # Add groups required
    if (($membercontrol == CONTROLMAPMANDATORY)
       || (($othercontrol == CONTROLMAPMANDATORY) 
220
            && (!Bugzilla->user->in_group($groupname)))) {
221 222 223 224
        # User had no option, bug needs to be in this group.
        push(@groupstoadd, $id)
    }
}
225

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
my @bug_fields = map {$_->name} Bugzilla->get_fields(
    { custom => 1, obsolete => 0, enter_bug => 1});
push(@bug_fields, qw(
    product
    component

    assigned_to
    qa_contact

    alias
    bug_file_loc
    bug_severity
    bug_status
    short_desc
    op_sys
    priority
    rep_platform
    version
    target_milestone
    status_whiteboard

    estimated_time
    deadline
));
my %bug_params;
foreach my $field (@bug_fields) {
    $bug_params{$field} = $cgi->param($field);
}
$bug_params{'creation_ts'} = $timestamp;

256
# Add the bug report to the DB.
257 258
$dbh->bz_lock_tables('bugs WRITE', 'bug_group_map WRITE', 'longdescs WRITE',
                     'cc WRITE', 'keywords WRITE', 'dependencies WRITE',
259 260
                     'bugs_activity WRITE', 'groups READ',
                     'user_group_map READ', 'group_group_map READ',
261 262 263 264
                     'keyworddefs READ', 'fielddefs READ',
                     'products READ', 'versions READ', 'milestones READ',
                     'components READ', 'profiles READ', 'bug_severity READ',
                     'op_sys READ', 'priority READ', 'rep_platform READ');
265

266
my $bug = Bugzilla::Bug->create(\%bug_params);
267

268
# Get the bug ID back.
269
my $id = $bug->bug_id;
terry%netscape.com's avatar
terry%netscape.com committed
270

271
# Add the group restrictions
272 273
my $sth_addgroup = $dbh->prepare(q{
            INSERT INTO bug_group_map (bug_id, group_id) VALUES (?, ?)});
274
foreach my $grouptoadd (@groupstoadd) {
275
    $sth_addgroup->execute($id, $grouptoadd);
276 277
}

278 279
# Add the initial comment, allowing for the fact that it may be private
my $privacy = 0;
280
if (Bugzilla->params->{"insidergroup"} 
281
    && Bugzilla->user->in_group(Bugzilla->params->{"insidergroup"})) 
282
{
283
    $privacy = $cgi->param('commentprivacy') ? 1 : 0;
284 285
}

286 287 288 289
trick_taint($comment);
$dbh->do(q{INSERT INTO longdescs (bug_id, who, bug_when, thetext,isprivate)
           VALUES (?, ?, ?, ?, ?)}, undef, ($id, $user->id, $timestamp,
                                            $comment, $privacy));
terry%netscape.com's avatar
terry%netscape.com committed
290

291
# Insert the cclist into the database
292
my $sth_cclist = $dbh->prepare(q{INSERT INTO cc (bug_id, who) VALUES (?,?)});
293
foreach my $ccid (@$cc_ids) {
294
    $sth_cclist->execute($id, $ccid);
terry%netscape.com's avatar
terry%netscape.com committed
295 296
}

297
my @all_deps;
298 299
my $sth_addkeyword = $dbh->prepare(q{
            INSERT INTO keywords (bug_id, keywordid) VALUES (?, ?)});
300
if (Bugzilla->user->in_group("editbugs")) {
301
    foreach my $keyword (@keyword_ids) {
302
        $sth_addkeyword->execute($id, $keyword);
303
    }
304
    if (@keyword_ids) {
305
        # Make sure that we have the correct case for the kw
306
        my $kw_ids = join(', ', @keyword_ids);
307
        my $list = $dbh->selectcol_arrayref(qq{
308 309
                                    SELECT name 
                                      FROM keyworddefs 
310 311
                                     WHERE id IN ($kw_ids)
                                  ORDER BY name});
312 313 314 315
        my $kw_list = join(', ', @$list);
        $dbh->do(q{UPDATE bugs 
                      SET delta_ts = ?, keywords = ? 
                    WHERE bug_id = ?}, undef, ($timestamp, $kw_list, $id));
316
    }
317
    if ($cgi->param('dependson') || $cgi->param('blocked')) {
318
        my %deps = (dependson => $depends_on_ids, blocked => $blocks_ids);
319 320
        foreach my $pair (["blocked", "dependson"], ["dependson", "blocked"]) {
            my ($me, $target) = @{$pair};
321 322
            my $sth_dep = $dbh->prepare(qq{
                        INSERT INTO dependencies ($me, $target) VALUES (?, ?)});
323
            foreach my $i (@{$deps{$target}}) {
324
                $sth_dep->execute($id, $i);
325 326
                push(@all_deps, $i); # list for mailing dependent bugs
                # Log the activity for the other bug:
327
                LogActivityEntry($i, $me, "", $id, $user->id, $timestamp);
328 329 330
            }
        }
    }
331 332
}

333 334 335 336 337 338 339
# All fields related to the newly created bug are set.
# The bug can now be made accessible.
$dbh->do("UPDATE bugs SET creation_ts = ? WHERE bug_id = ?",
          undef, ($timestamp, $id));

$dbh->bz_unlock_tables();

340 341 342
# We don't have to check if the user can see the bug, because a user filing
# a bug can always see it. You can't change reporter_accessible until
# after the bug is filed.
343

344 345 346 347
# Add an attachment if requested.
if (defined($cgi->upload('data')) || $cgi->param('attachurl')) {
    $cgi->param('isprivate', $cgi->param('commentprivacy'));
    Bugzilla::Attachment->insert_attachment_for_bug(!THROW_ERROR,
348
                                                    $bug, $user, $timestamp,
349 350 351 352 353 354 355 356 357 358
                                                    \$vars)
        || ($vars->{'message'} = 'attachment_creation_failed');

    # Determine if Patch Viewer is installed, for Diff link
    eval {
        require PatchReader;
        $vars->{'patchviewerinstalled'} = 1;
    };
}

359 360
# Add flags, if any. To avoid dying if something goes wrong
# while processing flags, we will eval() flag validation.
361
# This requires errors to die().
362 363
# XXX: this can go away as soon as flag validation is able to
#      fail without dying.
364 365
my $error_mode_cache = Bugzilla->error_mode;
Bugzilla->error_mode(ERROR_MODE_DIE);
366
eval {
367
    Bugzilla::Flag::validate($cgi, $id);
368 369
    Bugzilla::Flag::process($bug, undef, $timestamp, $cgi);
};
370
Bugzilla->error_mode($error_mode_cache);
371 372 373 374 375
if ($@) {
    $vars->{'message'} = 'flag_creation_failed';
    $vars->{'flag_creation_error'} = $@;
}

376
# Email everyone the details of the new bug 
377
$vars->{'mailrecipients'} = {'changer' => $user->login};
378

379
$vars->{'id'} = $id;
380
$vars->{'bug'} = $bug;
terry%netscape.com's avatar
terry%netscape.com committed
381

382
ThrowCodeError("bug_error", { bug => $bug }) if $bug->error;
383 384 385 386 387 388

$vars->{'sentmail'} = [];

push (@{$vars->{'sentmail'}}, { type => 'created',
                                id => $id,
                              });
389

390
foreach my $i (@all_deps) {
391
    push (@{$vars->{'sentmail'}}, { type => 'dep', id => $i, });
392
}
393

394
my @bug_list;
395 396
if ($cgi->cookie("BUGLIST")) {
    @bug_list = split(/:/, $cgi->cookie("BUGLIST"));
397
}
398
$vars->{'bug_list'} = \@bug_list;
399
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
400

401 402 403 404 405 406
if ($token) {
    trick_taint($token);
    $dbh->do('UPDATE tokens SET eventdata = ? WHERE token = ?', undef, 
             ("createbug:$id", $token));
}

407
print $cgi->header();
408 409
$template->process("bug/create/created.html.tmpl", $vars)
  || ThrowTemplateError($template->error());
410

411