#!/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 # Gregor Fischer # Klaas Freitag # Seth Landsman ############################################################### # 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.15 2003/01/25 04:39:14 jake%bugzilla.org 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) # 02/13/2000 (SML) # - email transformation code. # EMAIL_TRANSFORM_NONE does exact email matches # EMAIL_TRANSFORM_NAME_ONLY matches on the username # EMAIL_TRANSFORM_BASE_DOMAIN matches on the username and checks the domain of # to see that the one in the database is a subset of the one in the sender address # this is probably prone to false positives and probably needs more work. # 03/07/2000 (SML) # - added in $DEFAULT_PRODUCT and $DEFAULT_COMPONENT. i.e., if $DEFAULT_PRODUCT = "PENDING", # any email submitted bug will be entered with a product of PENDING, if no other product is # specified in the email. # Next round of revisions : # - 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 # - gpg signatures for security use strict; use MIME::Parser; chdir '..'; # this script lives in contrib push @INC, "contrib/."; push @INC, "."; require "globals.pl"; require "BugzillaEmail.pm"; 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; # change to use default product / component functionality my $DEFAULT_PRODUCT = "PENDING"; my $DEFAULT_COMPONENT = "PENDING"; ############################################################### # 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; # } return findUser($Name); } ############################################################### # Check if product is valid. sub CheckProduct { my $Product = shift; SendSQL("select name from products where name = " . SqlQuote($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 components.name from components, products where components.product_id = products.id AND products.name=" . SqlQuote($Product) . " and components.name=" . 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, products where versions.product_id = products.id AND products.name=" . 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\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( $prio eq "" || (lsearch( \@all_prios, $prio ) == -1) ) { # 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 = < _____ 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 ||= $entity->head->recommended_filename; $des ||= "unnamed attachment"; 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 <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 ); ConnectToDatabase(); $SenderShort = $Sender; $SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/; $SenderShort = findUser($SenderShort); print "SenderShort is $SenderShort\n"; if (!defined($SenderShort)) { $SenderShort = $Sender; $SenderShort =~ s/^.*?([a-zA-Z0-9_.-]+?\@[a-zA-Z0-9_.-]+\.[a-zA-Z0-9_.-]+).*$/$1/; } print "The sendershort is now $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; # 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 if (Param("useqacontact")) { SendSQL("select initialqacontact from components, products where components.product_id = products.id AND products.name=" . SqlQuote($Control{'product'}) . " and components.name=" . SqlQuote($Control{'component'})); $Control{'qacontact'} = FetchOneColumn(); } # Set Assigned - assigned_to depends on the product, cause initialowner # depends on the product ! # => first check product ! # Product my @all_products = (); # set to the default product. If the default product is empty, this has no effect my $Product = $DEFAULT_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 name from products ORDER BY name"); @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: # # set to the default component. If the default component is empty, this has no effect my $Component = $DEFAULT_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 components.name FROM components, products WHERE components.product_id=products.id AND products.name = " . 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 a value was given in the e-mail, convert it to an ID, # otherwise, retrieve it from the database. if ( defined($Control{'assigned_to'}) && $Control{'assigned_to'} !~ /^\s*$/ ) { $Control{'assigned_to'} = DBname_to_id($Control{'assigned_to'}); } else { SendSQL("select initialowner from components, products where " . " components.product_id=products.id AND products.name=" . SqlQuote($Control{'product'}) . " and components.name=" . SqlQuote($Control{'component'})); $Control{'assigned_to'} = FetchOneColumn(); } 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, products where versions.product_id=products.id AND products.name=" . 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 skipped !\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 = < 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 = <