summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/bug_email.pl1258
1 files changed, 1258 insertions, 0 deletions
diff --git a/contrib/bug_email.pl b/contrib/bug_email.pl
new file mode 100755
index 000000000..37e5055dd
--- /dev/null
+++ b/contrib/bug_email.pl
@@ -0,0 +1,1258 @@
+#!/usr/bin/perl -w
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# 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): Terry Weissman <terry@mozilla.org>
+# Gregor Fischer <fischer@suse.de>
+# Klaas Freitag <freitag@suse.de>
+# Seth Landsman <seth@dworkin.net>
+###############################################################
+# Bugzilla: Create a new bug via email
+###############################################################
+# The email needs to be feeded to this program on STDIN.
+# This is usually done by having an entry like this in your
+# .procmailrc:
+#
+# BUGZILLA_HOME=/usr/local/httpd/htdocs/bugzilla
+# :0 c
+# |(cd $BUGZILLA_HOME/contrib; ./bug_email.pl)
+#
+#
+# Installation note:
+#
+# You need to work with bug_email.pl the MIME::Parser installed.
+#
+# $Id: bug_email.pl,v 1.1 2000/02/12 16:13:01 seth%cs.brandeis.edu Exp $
+###############################################################
+
+# 02/12/2000 (SML)
+# - updates to work with most recent database changes to the bugs database
+# - updated so that it works out of bugzilla/contrib
+# - initial checkin into the mozilla CVS tree (yay)
+
+# Next round of revisions :
+# - canonical email transformation (i.e., seth@job.cs.brandeis.edu == seth@cs.brandeis.edu)
+# - default product and component (i.e., if you don't specify a product and component, it goes into a PENDING product)
+# - querying a bug over email
+# - appending a bug over email
+# - keywords over email
+# - use the globals.pl parameters functionality to edit and save this script's parameters
+# - integrate some setup in the checksetup.pl script
+
+# FWIW, the first two things are necessary for this to be useful in my setup, so they get an overwhelming bit of priority
+
+use diagnostics;
+use strict;
+use MIME::Parser;
+
+push @INC, "../."; # this script now lives in contrib
+
+require "globals.pl";
+
+my @mailerrors = (); # Buffer for Errors in the mail
+my @mailwarnings = (); # Buffer for Warnings found in the mail
+my $critical_err = 0; # Counter for critical errors - must be zero for success
+my %Control;
+my $Header = "";
+my @RequiredLabels = ();
+my @AllowedLabels = ();
+my $Body = "";
+my @attachments = ();
+
+my $product_valid = 0;
+my $test = 0;
+my $restricted = 0;
+my $SenderShort;
+my $Message_ID;
+
+###############################################################
+# storeAttachments
+#
+# in this sub, attachments found in the dump-sub will be written to
+# the database. The info, which attachments need saving is stored
+# in the global @attachments-list.
+# The sub returns the number of stored attachments.
+sub storeAttachments( $$ )
+{
+ my ($bugid, $submitter_id ) = @_;
+ my $maxsize = 0;
+ my $data;
+ my $listref = \@attachments;
+ my $att_count = 0;
+
+ $submitter_id ||= 0;
+
+ foreach my $pairref ( @$listref ) {
+ my ($decoded_file, $mime, $on_disk, $description) = @$pairref;
+
+
+ # Size check - mysql has a maximum space for the data ?
+ $maxsize = 1047552; # should be queried by a system( "mysqld --help" );,
+ # but this seems not to be supported by all current mysql-versions
+
+ # Read data file binary
+ if( $on_disk ) {
+ if( open( FILE, "$decoded_file" )) {
+ binmode FILE;
+ read FILE, $data, $maxsize;
+ close FILE;
+ $att_count ++;
+ } else {
+ print "Error while reading attachment $decoded_file!\n";
+ next;
+ }
+ # print "unlinking data/mimedump-tmp/$decoded_file";
+ # unlink "data/mimedump-tmp/$decoded_file";
+ } else {
+ # data is in the scalar
+ $data = $decoded_file;
+ }
+
+
+ # Make SQL-String
+ my $sql = "insert into attachments (bug_id, creation_ts, description, mimetype, ispatch, filename, thedata, submitter_id) values (";
+ $sql .= "$bugid, now(), " . SqlQuote( $description ) . ", ";
+ $sql .= SqlQuote( $mime ) . ", ";
+ $sql .= "0, ";
+ $sql .= SqlQuote( $decoded_file ) . ", ";
+ $sql .= SqlQuote( $data ) . ", ";
+ $sql .= "$submitter_id );";
+ SendSQL( $sql ) unless( $test );
+ }
+
+ return( $att_count );
+}
+
+
+
+###############################################################
+# Beautification
+sub horLine( )
+{
+ return( "-----------------------------------------------------------------------\n" );
+}
+
+
+###############################################################
+# Check if $Name is in $GroupName
+
+# This is no more CreateBugs group, so I'm using this routine to just determine if the user is
+# in the database. Eventually, here should be a seperate routine or renamed, or something (SML)
+sub CheckPermissions {
+ my ($GroupName, $Name) = @_;
+
+# SendSQL("select login_name from profiles,groups where groups.name='$GroupName' and profiles.groupset & groups.bit = groups.bit and profiles.login_name=\'$Name\'");
+# my $NewName = FetchOneColumn();
+# if ( $NewName eq $Name ) {
+# return $Name;
+# } else {
+# return;
+# }
+ my $query = "SELECT login_name FROM profiles WHERE profiles.login_name=\'$Name\'";
+ SendSQL($query);
+ my $check_name = FetchOneColumn();
+ if ($check_name eq $Name) {
+ return $Name;
+ } else {
+ return;
+ }
+}
+
+###############################################################
+# Check if product is valid.
+sub CheckProduct {
+ my $Product = shift;
+
+ SendSQL("select product from products where product='$Product'");
+ my $Result = FetchOneColumn();
+ if (lc($Result) eq lc($Product)) {
+ return $Result;
+ } else {
+ return "";
+ }
+}
+
+###############################################################
+# Check if component is valid for product.
+sub CheckComponent {
+ my $Product = shift;
+ my $Component = shift;
+
+ SendSQL("select value from components where program=" . SqlQuote($Product) . " and value=" . SqlQuote($Component) . "");
+ my $Result = FetchOneColumn();
+ if (lc($Result) eq lc($Component)) {
+ return $Result;
+ } else {
+ return "";
+ }
+}
+
+###############################################################
+# Check if component is valid for product.
+sub CheckVersion {
+ my $Product = shift;
+ my $Version = shift;
+
+ SendSQL("select value from versions where program=" . SqlQuote($Product) . " and value=" . SqlQuote($Version) . "");
+ my $Result = FetchOneColumn();
+ if (lc($Result) eq lc($Version)) {
+ return $Result;
+ } else {
+ return "";
+ }
+}
+
+###############################################################
+# Reply to a mail.
+sub Reply( $$$$ ) {
+ my ($Sender, $MessageID, $Subject, $Text) = @_;
+
+
+ die "Cannot find sender-email-address" unless defined( $Sender );
+
+ if( $test ) {
+ open( MAIL, ">>data/bug_email_test.log" );
+ }
+ else {
+ open( MAIL, "| /usr/sbin/sendmail -t" );
+ }
+
+ print MAIL "To: $Sender\n";
+ print MAIL "From: Bugzilla Mailinterface<yourmail\@here.com>\n";
+ print MAIL "Subject: $Subject\n";
+ print MAIL "In-Reply-To: $MessageID\n" if ( defined( $MessageID ));
+ print MAIL "\n";
+ print MAIL "$Text";
+ close( MAIL );
+
+}
+
+
+###############################################################
+# getEnumList
+# Queries the Database for the table description and figures the
+# enum-settings out - usefull for checking fields for enums like
+# prios
+sub getEnumList( $ )
+{
+ my ($fieldname) = @_;
+ SendSQL( "describe bugs $fieldname" );
+ my ($f, $type) = FetchSQLData();
+
+ # delete unneeded stuff
+ $type =~ s/enum\(|\)//g;
+ $type =~ s/\',//g;
+
+ my @all_prios = split( /\'/, $type );
+ return( @all_prios );
+}
+
+###############################################################
+# CheckPriority
+# Checks, if the priority setting is one of the enums defined
+# in the data base
+# Uses the global var. $Control{ 'priority' }
+sub CheckPriority
+{
+ my $prio = ($Control{'priority'} ||= "");
+ my @all_prios = getEnumList( "priority" );
+
+ if( (lsearch( \@all_prios, $prio ) == -1) || $prio eq "" ) {
+ # OK, Prio was not defined - create Answer
+ my $Text = "You sent wrong priority-setting, valid values are:" .
+ join( "\n\t", @all_prios ) . "\n\n";
+ $Text .= "* The priority is set to the default value ".
+ SqlQuote( Param('defaultpriority')) . "\n";
+
+ BugMailError( 0, $Text );
+
+ # set default value from param-file
+ $Control{'priority'} = Param( 'defaultpriority' );
+ } else {
+ # Nothing to do
+ }
+}
+
+###############################################################
+# CheckSeverity
+# checks the bug_severity
+sub CheckSeverity
+{
+ my $sever = ($Control{'bug_severity'} ||= "" );
+ my @all_sever = getEnumList( "bug_severity" );
+
+ if( (lsearch( \@all_sever, $sever ) == -1) || $sever eq "" ) {
+ # OK, Prio was not defined - create Answer
+ my $Text = "You sent wrong bug_severity-setting, valid values are:" .
+ join( "\n\t", @all_sever ) . "\n\n";
+ $Text .= "* The bug_severity is set to the default value ".
+ SqlQuote( "normal" ) . "\n";
+
+ BugMailError( 0, $Text );
+
+ # set default value from param-file
+ $Control{'bug_severity'} = "normal";
+ }
+}
+
+###############################################################
+# CheckArea
+# checks the area-field
+sub CheckArea
+{
+ my $area = ($Control{'area'} ||= "" );
+ my @all= getEnumList( "area" );
+
+ if( (lsearch( \@all, $area ) == -1) || $area eq "" ) {
+ # OK, Area was not defined - create Answer
+ my $Text = "You sent wrong area-setting, valid values are:" .
+ join( "\n\t", @all ) . "\n\n";
+ $Text .= "* The area is set to the default value ".
+ SqlQuote( "BUILD" ) . "\n";
+
+ BugMailError( 0, $Text );
+
+ # set default value from param-file
+ $Control{'area'} = "BUILD";
+ }
+}
+
+###############################################################
+# CheckPlatform
+# checks the given Platform and corrects it
+sub CheckPlatform
+{
+ my $platform = ($Control{'rep_platform'} ||= "" );
+ my @all = getEnumList( "rep_platform" );
+
+ if( (lsearch( \@all, $platform ) == -1) || $platform eq "" ) {
+ # OK, Prio was not defined - create Answer
+ my $Text = "You sent wrong platform-setting, valid values are:" .
+ join( "\n\t", @all ) . "\n\n";
+ $Text .= "* The rep_platform is set to the default value ".
+ SqlQuote( "All" ) . "\n";
+
+ BugMailError( 0, $Text );
+
+ # set default value from param-file
+ $Control{'rep_platform'} = "All";
+ }
+}
+
+###############################################################
+# CheckSystem
+# checks the given Op-Sys and corrects it
+sub CheckSystem
+{
+ my $sys = ($Control{'op_sys'} ||= "" );
+ my @all = getEnumList( "op_sys" );
+
+ if( (lsearch( \@all, $sys ) == -1) || $sys eq "" ) {
+ # OK, Prio was not defined - create Answer
+ my $Text = "You sent wrong OS-setting, valid values are:" .
+ join( "\n\t", @all ) . "\n\n";
+ $Text .= "* The op_sys is set to the default value ".
+ SqlQuote( "Linux" ) . "\n";
+
+ BugMailError( 0, $Text );
+
+ # set default value from param-file
+ $Control{'op_sys'} = "Linux";
+ }
+}
+
+
+###############################################################
+# Fetches all lines of a query with a single column selected and
+# returns it as an array
+#
+sub FetchAllSQLData( )
+{
+ my @res = ();
+
+ while( MoreSQLData() ){
+ push( @res, FetchOneColumn() );
+ }
+ return( @res );
+}
+
+###############################################################
+# Error Handler for Errors in the mail
+#
+# This function can be called multiple within processing one mail and
+# stores the errors found in the Mail. Errors are for example empty
+# required tags, missing required tags and so on.
+#
+# The benefit is, that the mail users get a reply, where all mail errors
+# are reported. The reply mail includes all messages what was wrong and
+# the second mail the user sends can be ok, cause all his faults where
+# reported.
+#
+# BugMailError takes two arguments: The first one is a flag, how heavy
+# the error is:
+#
+# 0 - Its an error, but bugzilla can process the bug. The user should
+# handle that as a warning.
+#
+# 1 - Its a real bug. Bugzilla cant store the bug. The mail has to be
+# resent.
+#
+# 2 - Permission error: The user does not have the permission to send
+# a bug.
+#
+# The second argument is a Text which describs the bug.
+#
+#
+# #
+sub BugMailError($ $ )
+{
+ my ( $errflag, $text ) = @_;
+
+ # On permission error, dont sent all other Errors back -> just quit !
+ if( $errflag == 2 ) { # Permission-Error
+ Reply( $SenderShort, $Message_ID, "Bugzilla Error", "Permission denied.\n\n" .
+ "You do not have the permissions to create a new bug. Sorry.\n" );
+ exit;
+ }
+
+
+ # Warnings - store for the reply mail
+ if( $errflag == 0 ) {
+ push( @mailwarnings, $text );
+ }
+
+ # Critical Error
+ if( $errflag == 1 ) {
+ $critical_err += 1;
+ push( @mailerrors, $text );
+ }
+}
+
+###############################################################
+# getWarningText()
+#
+# getWarningText() returns a reply-ready Textline of all the
+# Warnings in the Mail
+sub getWarningText()
+{
+ my $anz = @mailwarnings;
+
+ my $ret = <<END
+
+The Bugzilla Mail Interface found warnings (JFYI):
+
+END
+ ;
+
+ # Handshake if no warnings at all
+ return( "\n\n Your mail was processed without Warnings !\n" ) if( $anz == 0 );
+
+ # build a text
+ $ret .= join( "\n ", @mailwarnings );
+ return( horLine() . $ret );
+}
+
+sub getErrorText()
+{
+ my $anz = @mailerrors;
+
+ my $ret = <<END
+
+************************** ERROR **************************
+
+Your request to the Bugzilla mail interface could not be met
+due to errors in the mail. We will find it !
+
+
+END
+ ;
+ return( "\n\n Your mail was processed without errors !\n") if( $anz == 0 );
+ # build a text
+ $ret .= join( "\n ", @mailerrors );
+ return( $ret );
+}
+
+###############################################################
+# generateTemplate
+#
+# This functiuon generates a mail-Template with the
+sub generateTemplate()
+{
+ my $w;
+ my $ret;
+
+ # Required Labels
+ $ret =<<EOF
+
+
+You may want to use this template to resend your mail. Please fill in the missing
+keys.
+
+_____ snip _______________________________________________________________________
+
+EOF
+ ;
+ foreach ( @RequiredLabels ) {
+ $w = "";
+ $w = $Control{$_} if defined( $Control{ $_ } );
+ $ret .= sprintf( " \@%-15s: %s\n", $_, $w );
+ }
+
+ $ret .= "\n";
+ # Allowed Labels
+ foreach( @AllowedLabels ) {
+ next if( /reporter/ ); # Reporter is not a valid label
+ next if( /assigned_to/ ); # Assigned to is just a number
+ if( defined( $Control{ $_ } ) && lsearch( \@RequiredLabels, $_ ) == -1 ) {
+ $ret .= sprintf( " \@%-15s: %s\n", $_, $Control{ $_ } );
+ }
+ }
+
+ if( $Body eq "" ) {
+ $ret .= <<END
+
+ < the bug-description follows here >
+
+_____ snip _______________________________________________________________________
+
+END
+ ; } else {
+ $ret .= "\n" . $Body;
+ }
+
+ return( $ret );
+
+}
+###############################################################
+# groupBitToString( $ )
+# converts a given number back to the groupsetting-names
+# This function accepts single numbers as added bits or
+# Strings with +-Signs
+sub groupBitToString( $ )
+{
+ my ($bits) = @_;
+ my $type;
+ my @bitlist = ();
+ my $ret = "";
+
+ if( $bits =~ /^\d+$/ ) { # only numbers
+ $type = 1;
+
+ } elsif( $bits =~ /^(\s*\d+\s*\+\s*)+/ ) {
+ $type = 2;
+ } else {
+ # Error: unknown format !
+ $type = 0;
+ }
+
+ $bits =~ s/\s*//g;
+ #
+ # Query for groupset-Information
+ SendSQL( "Select Bit,Name, Description from groups where isbuggroup=1" );
+ my @line;
+ while( MoreSQLData() ){
+ @line = FetchSQLData();
+
+ if( $type == 1 ) {
+ if( ((0+$bits) & (0+$line[0])) == 0+$line[0] ) {
+ $ret .= sprintf( "%s ", $line[1] );
+ }
+ } elsif( $type == 2 ) {
+ if( $bits =~ /$line[0]/ ) {
+ $ret .= sprintf( "%s ", $line[1] );
+ }
+ }
+ }
+
+ return( $ret );
+}
+
+
+
+#------------------------------
+#
+# dump_entity ENTITY, NAME
+#
+# Recursive routine for parsing a mime coded mail.
+# One mail may contain more than one mime blocks, which need to be
+# handled. Therefore, this function is called recursively.
+#
+# It gets the for bugzilla important information from the mailbody and
+# stores them into the global attachment-list @attachments. The attachment-list
+# is needed in storeAttachments.
+#
+sub dump_entity {
+ my ($entity, $name) = @_;
+ defined($name) or $name = "'anonymous'";
+ my $IO;
+
+
+ # Output the body:
+ my @parts = $entity->parts;
+ if (@parts) { # multipart...
+ my $i;
+ foreach $i (0 .. $#parts) { # dump each part...
+ dump_entity($parts[$i], ("$name, part ".(1+$i)));
+ }
+ } else { # single part...
+
+ # Get MIME type, and display accordingly...
+ my $msg_part = $entity->head->get( 'Content-Disposition' );
+
+ $msg_part ||= "";
+
+ my ($type, $subtype) = split('/', $entity->head->mime_type);
+ my $body = $entity->bodyhandle;
+ my ($data, $on_disk );
+
+ if( $msg_part =~ /^attachment/ ) {
+ # Attached File
+ my $des = $entity->head->get('Content-Description');
+ $des ||= "";
+
+ if( defined( $body->path )) { # Data is on disk
+ $on_disk = 1;
+ $data = $body->path;
+
+ } else { # Data is in core
+ $on_disk = 0;
+ $data = $body->as_string;
+ }
+ push ( @attachments, [ $data, $entity->head->mime_type, $on_disk, $des ] );
+ } else {
+ # Real Message
+ if ($type =~ /^(text|message)$/) { # text: display it...
+ if ($IO = $body->open("r")) {
+ $Body .= $_ while (defined($_ = $IO->getline));
+ $IO->close;
+ } else { # d'oh!
+ print "$0: couldn't find/open '$name': $!";
+ }
+ } else { print "Oooops - no Body !\n"; }
+ }
+ }
+}
+
+###############################################################
+# sub extractControls
+###############################################################
+#
+# This sub parses the message Body and filters the control-keys.
+# Attention: Global hash Controls affected
+#
+sub extractControls( $ )
+{
+ my ($body) = @_;
+ my $backbody = "";
+
+ my @lbody = split( /\n/, $body );
+
+ # In restricted mode, all lines before the first keyword
+ # are skipped.
+ if( $restricted ) {
+ while( $lbody[0] =~ /^\s*\@.*/ ){ shift( @lbody );}
+ }
+
+ # Filtering for keys
+ foreach( @lbody ) {
+ if( /^\s*\@description/ ) {
+ s/\s*\@description//;
+ $backbody .= $_;
+ } elsif( /^\s*\@(.*?)(?:\s*=\s*|\s*:\s*|\s+)(.*?)\s*$/ ) {
+ $Control{lc($1)} = $2;
+ } else {
+ $backbody .= "$_" . "\n";
+ }
+ }
+
+ # thats it.
+ return( $backbody );
+}
+
+###############################################################
+# Main starts here
+###############################################################
+#
+# Commandline switches:
+# -t: test mode - no DB-Inserts
+foreach( @ARGV ) {
+ $restricted = 1 if ( /-r/ );
+ $test = 1 if ( /-t/ );
+}
+
+#
+# Parsing a mime-message
+#
+if( -t STDIN ) {
+print STDERR <<END
+ Bugzilla Mail Interface
+
+ This scripts reads a mail message through stdin and parses the message,
+ for to insert a bug to bugzilla.
+
+ Options
+ -t: Testmode - No insert to the DB, but logfile
+ -r: restricted mode - all lines before the keys in the mail are skipped
+
+END
+ ;
+exit;
+}
+
+
+# Create a new MIME parser:
+my $parser = new MIME::Parser;
+
+# Create and set the output directory:
+# FIXME: There should be a $BUGZILLA_HOME variable (SML)
+(-d "../data/mimedump-tmp") or mkdir "../data/mimedump-tmp",0755 or die "mkdir: $!";
+(-w "../data/mimedump-tmp") or die "can't write to directory";
+
+$parser->output_dir("../data/mimedump-tmp");
+
+# Read the MIME message:
+my $entity = $parser->read(\*STDIN) or die "couldn't parse MIME stream";
+$entity->remove_sig(10); # Removes the signature in the last 10 lines
+
+# Getting values from parsed mail
+my $Sender = $entity->get( 'From' );
+$Sender ||= $entity->get( 'Reply-To' );
+$Message_ID = $entity->get( 'Message-Id' );
+
+die (" *** Cant find Sender-adress in sent mail ! ***\n" ) unless defined( $Sender );
+chomp( $Sender );
+chomp( $Message_ID );
+
+
+$SenderShort = $Sender;
+$SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/;
+
+# FIXME: run sender short via a method to get a canonical email address (SML)
+#print "Sender short is $SenderShort\n";
+
+my $Subject = "";
+$Subject = $entity->get( 'Subject' );
+chomp( $Subject );
+
+# Get all the attachments
+dump_entity($entity);
+# print $Body;
+$Body = extractControls( $Body ); # fills the Control-Hash
+
+if( $test ) {
+ foreach (keys %Control ) {
+ print "$_ => $Control{$_}\n";
+ }
+}
+
+$Control{'short_desc'} ||= $Subject;
+#
+# * Mailparsing finishes here *
+#
+
+######################################################################
+# Now a lot of Checks of the given Labels start.
+# Check Control-Labels
+# not: reporter !
+@AllowedLabels = ("product", "version", "rep_platform",
+ "bug_severity", "priority", "op_sys", "assigned_to",
+ "bug_status", "bug_file_loc", "short_desc", "component",
+ "status_whiteboard", "target_milestone", "groupset",
+ "qa_contact");
+#my @AllowedLabels = qw{Summary priority platform assign};
+foreach (keys %Control) {
+ if ( lsearch( \@AllowedLabels, $_) < 0 ) {
+ BugMailError( 0, "You sent a unknown label: " . $_ );
+ }
+}
+
+push( @AllowedLabels, "reporter" );
+$Control{'reporter'} = $SenderShort;
+
+# Check required Labels - not all labels are required, because they could be generated
+# from the given information
+# Just send a warning- the error-Flag will be set later
+@RequiredLabels = qw{product version component short_desc};
+foreach my $Label (@RequiredLabels) {
+ if ( ! defined $Control{$Label} ) {
+ BugMailError( 0, "You were missing a required label: \@$Label\n" );
+ next;
+ }
+
+ if( $Control{$Label} =~ /^\s*$/ ) {
+ BugMailError( 0, "One of your required labels is empty: $Label" );
+ next;
+ }
+}
+
+if ( $Body =~ /^\s*$/s ) {
+ BugMailError( 1, "You sent a completely empty body !" );
+}
+
+
+# umask 0;
+ConnectToDatabase();
+
+# Check Permissions ...
+if (! CheckPermissions("CreateBugs", $SenderShort ) ) {
+ BugMailError( 2, "Permission denied.\n\n" .
+ "You do not have the permissions to create a new bug. Sorry.\n" );
+}
+
+# Set QA
+SendSQL("select initialqacontact from components where program=" .
+ SqlQuote($Control{'product'}) .
+ " and value=" . SqlQuote($Control{'component'}));
+my $qacontact = FetchOneColumn();
+if (defined $qacontact && $qacontact !~ /^\s*$/) {
+ #$Control{'qa_contact'} = DBNameToIdAndCheck($qacontact, 1);
+ $Control{'qa_contact'} = DBname_to_id($qacontact);
+
+ if ( ! $Control{'qa_contact'} ) {
+ BugMailError( 0, "Could not resolve qa_contact !\n" );
+ }
+
+ #push(@bug_fields, "qa_contact");
+}
+
+# Set Assigned - assigned_to depends on the product, cause initialowner
+# depends on the product !
+# => first check product !
+# Product
+my @all_products = ();
+my $Product = "";
+$Product = CheckProduct( $Control{'product'} ) if( defined( $Control{ 'product'} ));
+
+if ( $Product eq "" ) {
+ my $Text = "You didnt send a value for the required key \@product !\n\n";
+
+ $Text = "You sent the invalid product \"$Control{'product'}\"!\n\n"
+ if( defined( $Control{ 'product'} ));
+
+ $Text .= "Valid products are:\n\t";
+
+ SendSQL("select product from products");
+ @all_products = FetchAllSQLData();
+ $Text .= join( "\n\t", @all_products ) . "\n\n";
+ $Text .= horLine();
+
+ BugMailError( 1, $Text );
+} else {
+ # Fill list @all_products, which is needed in case of component-help
+ @all_products = ( $Product );
+ $product_valid = 1;
+}
+$Control{'product'} = $Product;
+
+#
+# Check the Component:
+#
+my $Component = "";
+
+if( defined( $Control{'component' } )) {
+ $Component = CheckComponent( $Control{'product'}, $Control{'component'} );
+}
+
+if ( $Component eq "" ) {
+
+ my $Text = "You did not send a value for the required key \@component!\n\n";
+
+ if( defined( $Control{ 'component' } )) {
+ $Text = "You sent the invalid component \"$Control{'component'}\" !\n";
+ }
+
+ #
+ # Attention: If no product was sent, the user needs info for all components of all
+ # products -> big reply mail :)
+ # if a product was sent, only reply the components of the sent product
+ my @val_components = ();
+ foreach my $prod ( @all_products ) {
+ $Text .= "\nValid components for product `$prod' are: \n\t";
+
+ SendSQL("select value from components where program=" . SqlQuote( $prod ) . "");
+ @val_components = FetchAllSQLData();
+
+ $Text .= join( "\n\t", @val_components ) . "\n";
+ }
+
+ # Special: if there is a valid product, maybe it has only one component -> use it !
+ #
+ my $amount_of_comps = @val_components;
+ if( $product_valid && $amount_of_comps == 1 ) {
+ $Component = $val_components[0];
+
+ $Text .= " * You did not send a component, but a valid product " . SqlQuote( $Product ) . ".\n";
+ $Text .= " * This product only has one component ". SqlQuote( $Component ) .".\n" .
+ " * This component was set by bugzilla for submitting the bug.\n\n";
+ BugMailError( 0, $Text ); # No blocker
+
+ } else { # The component is really buggy :(
+ $Text .= horLine();
+ BugMailError( 1, $Text );
+ }
+}
+$Control{'component'} = $Component;
+
+
+#
+# Check assigned_to
+# if no assigned_to was given, generate it from the product-DB
+my $forceAssignedOK = 0;
+if ( (! defined($Control{'assigned_to'}) )
+ || $Control{'assigned_to'} =~ /^\s*$/ ) {
+ SendSQL("select initialowner from components where program=" .
+ SqlQuote($Control{'product'}) .
+ " and value=" . SqlQuote($Control{'component'}));
+ $Control{'assigned_to'} = FetchOneColumn();
+ $forceAssignedOK = 1;
+}
+
+
+# Recode Names
+$Control{'assigned_to'} = DBname_to_id($Control{'assigned_to'}, $forceAssignedOK);
+
+if ( $Control{'assigned_to'} == 0 ) {
+ my $Text = "Could not resolve key \@assigned_to !\n" .
+ "If you do NOT send a value for assigned_to, the bug will be assigned to\n" .
+ "the qa-contact for the product and component.\n";
+ $Text .= "This works only if product and component are OK. \n"
+ . horLine();
+
+ BugMailError( 1, $Text );
+}
+
+
+$Control{'reporter'} = DBname_to_id($Control{'reporter'});
+if ( ! $Control{'reporter'} ) {
+ BugMailError( 1, "Could not resolve reporter !\n" );
+}
+
+### Set default values
+CheckPriority( );
+CheckSeverity( );
+CheckPlatform( );
+CheckSystem( );
+# CheckArea();
+
+### Check values ...
+# Version
+my $Version = "";
+$Version = CheckVersion( $Control{'product'}, $Control{'version'} ) if( defined( $Control{'version'}));
+if ( $Version eq "" ) {
+ my $Text = "You did not send a value for the required key \@version!\n\n";
+
+ if( defined( $Control{'version'})) {
+ my $Text = "You sent the invalid version \"$Control{'version'}\"!\n";
+ }
+
+ my $anz_versions;
+ my @all_versions;
+ # Assemble help text
+ foreach my $prod ( @all_products ) {
+ $Text .= "Valid versions for product " . SqlQuote( $prod ) . " are: \n\t";
+
+ SendSQL("select value from versions where program=" . SqlQuote( $prod ) . "");
+ @all_versions = FetchAllSQLData();
+ $anz_versions = @all_versions;
+ $Text .= join( "\n\t", @all_versions ) . "\n" ;
+
+ }
+
+ # Check if we could use the only version
+ if( $anz_versions == 1 && $product_valid ) {
+ $Version = $all_versions[0];
+ # Fine, there is only one version string
+ $Text .= " * You did not send a version, but a valid product " . SqlQuote( $Product ) . ".\n";
+ $Text .= " * This product has has only the one version ". SqlQuote( $Version) .".\n" .
+ " * This version was set by bugzilla for submitting the bug.\n\n";
+ $Text .= horLine();
+ BugMailError( 0, $Text ); # No blocker
+ } else {
+ $Text .= horLine();
+ BugMailError( 1, $Text );
+ }
+
+}
+
+$Control{'version'} = $Version;
+
+# GroupsSet: Protections for Bug info. This paramter controls the visiblility of the
+# given bug. An Error in the given Buggroup is not a blocker, a default is taken.
+#
+# The GroupSet is accepted in three ways: As single number like 65536
+# As added numbers like 65536 + 6 +8
+# As literals linked with whitespaces, plus-signs or kommas
+#
+my $GroupSet = "";
+$GroupSet = $Control{'groupset'} if( defined( $Control{ 'groupset' }));
+#
+# Fetch the default value for groupsetting
+SendSQL("select bit from groups where name=" . SqlQuote( "ReadInternal" ));
+my $default_group = FetchOneColumn();
+
+if( $GroupSet eq "" ) {
+ # To bad: Groupset does not contain anything -> set to default
+ $GroupSet = $default_group;
+ #
+ # Give the user a hint
+ my $Text = "You did not send a value for optional key \@groupset, which controls\n";
+ $Text .= "the Permissions of the bug. It was set to a default value 'Internal Bug'\n";
+ $Text .= "Probably the QA will change that if desired.\n";
+
+ BugMailError( 0, $Text );
+} elsif( $GroupSet =~ /^\d+$/ ) {
+ # Numerical Groups (no +-signs), the GroupSet must be the sum of the bits
+ #
+ my $grp_num = $GroupSet;
+ # print "Numeric: $GroupSet\n";
+ SendSQL("select bit from groups where isbuggroup=1 order by bit");
+ my @Groups = FetchAllSQLData();
+
+ # DANGEROUS: This code implies, that perl *CAN* cope with large numbers
+ # Its probably better to allow only one default-group when mailing !
+ my $Val = "0";
+ foreach ( @Groups ) {
+ # print 0+$grp_num & 0+$_ , "\n";
+ if ( ( (0+$grp_num) & (0+$_) ) == 0+$_ ) {
+ $Val .= sprintf( "+%d", $_ );
+ }
+ }
+ if( $Val eq "0" ) {
+ # No valid group found
+ my $Text = "The number you sent for the groupset of the bug was wrong.\n" .
+ "It was not the sum of valid bits, which are:\n\t";
+ $Text .= join( "\n\t", @Groups ) . "\n";
+ $Text .= "The groupset for your bug is set to default $default_group, which\n" .
+ "means 'ReadInternal'";
+
+ BugMailError( 0, $Text );
+ $GroupSet = $default_group;
+ } else {
+ $GroupSet = $Val;
+ }
+
+} elsif( $GroupSet =~ /^(\s*\d+\s*\+\s*)+/ ) {
+ #
+ # Groupset given as String with added numbers like 65536+131072
+ # The strings are splitted and checked if the numbers are in the DB
+ my @bits = split( /\s*\+\s*/, $GroupSet );
+ my $new_groupset = "0";
+ # Get all bits for groupsetting
+ SendSQL("select bit from groups where isbuggroup=1" );
+ my @db_bits = FetchAllSQLData();
+
+ # ... and check, if the given bits and the one in the DB fit together
+ foreach my $bit ( @bits ) {
+ # print "The Bit is: $bit \n";
+ if( lsearch( \@db_bits, $bit ) == -1 ) {
+ # Bit not found !
+ my $Text = "Checking the Group-Settings: You sent the Groupset-Bit $bit\n" .
+ "which is not a valid Groupset-Bit. It will be scipped !\n\n";
+ BugMailError( 0, $Text );
+ } else {
+ # Cool bit, add to the result-String
+ $new_groupset .= "+" . $bit;
+ }
+ }
+
+ # Is the new-String larger than 0
+ if( $new_groupset eq "0" ) {
+ $new_groupset = $default_group;
+
+ my $Text = "All given Groupsetting-Bits are invalid. Setting Groupsetting to\n" .
+ "default-Value $new_groupset, what means 'ReadInternal'\n\n";
+
+ BugMailError( 0, $Text );
+ }
+ # Restore to Groupset-Variable
+ $GroupSet = $new_groupset;
+
+} else {
+ # literal e.g. 'ReadInternal'
+ my $Value = "0";
+ my $gserr = 0;
+ my $Text = "";
+
+ #
+ # Split literal Groupsettings either on Whitespaces, +-Signs or ,
+ # Then search for every Literal in the DB - col name
+ foreach ( split /\s+|\s*\+\s*|\s*,\s*/, $GroupSet ) {
+ SendSQL("select bit, Name from groups where name=" . SqlQuote($_));
+ my( $bval, $bname ) = FetchSQLData();
+
+ if( defined( $bname ) && $_ eq $bname ) {
+ $Value .= sprintf( "+%d", $bval );
+ } else {
+ $Text .= "You sent the wrong GroupSet-String $_\n";
+ $gserr = 1;
+ }
+ }
+ #
+ # Give help if wrong GroupSet-String came
+ if( $gserr > 0 ) {
+ # There happend errors
+ $Text .= "Here are all valid literal Groupsetting-strings:\n\t";
+ SendSQL( "select name from groups where isbuggroup=1" );
+ $Text .= join( "\n\t", FetchAllSQLData()) . "\n";
+ BugMailError( 0, $Text );
+ }
+
+ #
+ # Check if anything was right, if not -> set default
+ if( $Value eq "0" ) {
+ $Value = $default_group;
+ $Text .= "\nThe group will be set to $default_group, what means 'ReadInternal'\n\n";
+ }
+
+ $GroupSet = $Value;
+} # End of checking groupsets
+
+$Control{'groupset'} = $GroupSet;
+
+# ###################################################################################
+# Checking is finished
+#
+
+# Check used fields
+my @used_fields;
+
+foreach my $f (@AllowedLabels) {
+ if ((exists $Control{$f}) && ($Control{$f} !~ /^\s*$/ )) {
+ push (@used_fields, $f);
+ }
+}
+
+#
+# Creating the query for inserting the bug
+# -> this should only be done, if there was no critical error before
+if( $critical_err == 0 )
+{
+
+ my $reply = <<END
+
+ +---------------------------------------------------------------------------+
+ B U G Z I L L A - M A I L - I N T E R F A C E
+ +---------------------------------------------------------------------------+
+
+ Your Bugzilla Mail Interface request was successfull.
+
+END
+;
+
+ $reply .= "Your Bug-ID is ";
+ my $reporter = "";
+
+ my $query = "insert into bugs (\n" . join(",\n", @used_fields ) .
+ ", bug_status, creation_ts) values ( ";
+
+ my $tmp_reply = "These values were stored by bugzilla:\n";
+ my $val;
+ foreach my $field (@used_fields) {
+ if( $field eq "groupset" ) {
+ $query .= $Control{$field} . ",\n";
+ } else {
+ $query .= SqlQuote($Control{$field}) . ",\n";
+ }
+
+ $val = $Control{ $field };
+
+ $val = DBID_to_name( $val ) if( $field =~ /reporter|assigned_to|qa_contact/ );
+ $val = groupBitToString( $val ) if( $field =~ /groupset/ );
+
+ $tmp_reply .= sprintf( " \@%-15s = %-15s\n", $field, $val );
+
+ if ($field eq "reporter") {
+ $reporter = $val;
+ }
+ }
+
+ $tmp_reply .= " ... and your error-description !\n";
+
+ my $comment = $Body;
+ $comment =~ s/\r\n/\n/g; # Get rid of windows-style line endings.
+ $comment =~ s/\r/\n/g; # Get rid of mac-style line endings.
+ $comment = trim($comment);
+
+ SendSQL("SELECT now()");
+ my $bug_when = FetchOneColumn();
+
+ $query .= SqlQuote( "NEW" ) . ", \'$bug_when\')\n";
+# $query .= SqlQuote( "NEW" ) . ", now(), " . SqlQuote($comment) . " )\n";
+
+ SendSQL("SELECT userid FROM profiles WHERE login_name=\'$reporter\'");
+ my $userid = FetchOneColumn();
+
+ my $id;
+
+ if( ! $test ) {
+ SendSQL($query);
+
+ SendSQL("select LAST_INSERT_ID()");
+ $id = FetchOneColumn();
+
+ my $long_desc_query = "INSERT INTO longdescs SET bug_id=$id, who=$userid, bug_when=\'$bug_when\', thetext=" . SqlQuote($comment);
+ SendSQL($long_desc_query);
+
+ # Cool, the mail was successfull
+ system("cd .. ; ./processmail $id '$Sender'");
+ } else {
+ $id = 0xFFFF; # TEST !
+ print "\n-------------------------------------------------------------------------\n";
+ print "$query\n";
+ }
+
+ #
+ # handle Attachments
+ #
+ my $attaches = storeAttachments( $id, $Control{'reporter'} );
+ $tmp_reply .= "\n\tYou sent $attaches attachment(s). \n" if( $attaches > 0 );
+
+ $reply .= $id . "\n\n" . $tmp_reply . "\n" . getWarningText();
+
+ $entity->purge(); # Removes all temp files
+
+ #
+ # Send the 'you did it'-reply
+ Reply( $SenderShort, $Message_ID,"Bugzilla success (ID $id)", $reply );
+
+} else {
+ # There were critical errors in the mail - the bug couldnt be inserted. !
+my $errreply = <<END
+
+ +---------------------------------------------------------------------------+
+ B U G Z I L L A - M A I L - I N T E R F A C E
+ +---------------------------------------------------------------------------+
+
+END
+ ;
+
+ $errreply .= getErrorText() . getWarningText() . generateTemplate();
+
+ Reply( $SenderShort, $Message_ID, "Bugzilla Error", $errreply );
+
+print getErrorText();
+ # print getWarningText();
+ # print generateTemplate();
+}
+
+
+
+
+
+exit;
+