]> git.donarmstrong.com Git - debbugs.git/blobdiff - scripts/service
Strip BOM before regexes involving spaces (closes: #488554)
[debbugs.git] / scripts / service
index b606745fda288f4d2056a5fd2c456dd633a5751d..94b2571b5a08dca6fdc121b47f8bba4c68fb81e2 100755 (executable)
@@ -17,7 +17,7 @@ use Params::Validate qw(:types validate_with);
 
 use Debbugs::Common qw(:util :quit :misc :lock);
 
-use Debbugs::Status qw(:read :status :write :versions);
+use Debbugs::Status qw(:read :status :write :versions :hook);
 
 use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522);
 use Debbugs::Mail qw(send_mail_message);
@@ -29,7 +29,7 @@ use Debbugs::Versions::Dpkg;
 use Debbugs::Status qw(splitpackages);
 
 use Debbugs::CGI qw(html_escape);
-use Debbugs::Control qw(:archive :log);
+use Debbugs::Control qw(:all);
 use Debbugs::Log qw(:misc);
 use Debbugs::Text qw(:templates);
 
@@ -84,6 +84,7 @@ for (@headerlines) {
     }
 }
 $header{'message-id'} ||= '';
+$header{subject} ||= '';
 
 grep(s/\s+$//,@bodylines);
 
@@ -107,7 +108,7 @@ if ( defined($header{'reply-to'}) && $header{'reply-to'} ne "" ) {
 
 # This is an error counter which should be incremented every time there is an error.
 my $errors = 0;
-my $controlrequestaddr= ($control ? 'control' : 'request').$config{email_domain};
+my $controlrequestaddr= ($control ? 'control' : 'request').'@'.$config{email_domain};
 my $transcript_scalar = '';
 my $transcript = IO::Scalar->new(\$transcript_scalar) or
      die "Unable to create new IO::Scalar";
@@ -157,6 +158,8 @@ our $action;
 
 # recipients of mail
 my %recipients;
+# affected_packages
+my %affected_packages;
 my $ok = 0;
 my $unknowns = 0;
 my $procline=0;
@@ -173,6 +176,9 @@ for ($procline=0; $procline<=$#bodylines; $procline++) {
         last;
     }
     $_= $bodylines[$procline]; s/\s+$//;
+    # Remove BOM markers from UTF-8 strings
+    # Fixes #488554
+    s/\xef\xbb\xbf//g;
     next unless m/\S/;
     print {$transcript} "> $_\n";
     next if m/^\s*\#/;
@@ -385,12 +391,14 @@ END
            Debbugs::User::read_usertags(\%ut, $user);
             my @oldtags = (); my @newtags = (); my @badtags = ();
            my %chtags;
-           for my $t (split /[,\s]+/, $tags) {
-               if ($t =~ m/^[a-zA-Z0-9.+\@-]+$/) {
-                   $chtags{$t} = 1;
-               } else {
-                   push @badtags, $t;
-               }
+           if (defined $tags and length $tags) {
+                for my $t (split /[,\s]+/, $tags) {
+                     if ($t =~ m/^[a-zA-Z0-9.+\@-]+$/) {
+                          $chtags{$t} = 1;
+                     } else {
+                          push @badtags, $t;
+                     }
+                }
            }
            if (@badtags) {
                 print {$transcript} "Ignoring illegal tag/s: ".join(', ', @badtags).".\nPlease use only alphanumerics, at, dot, plus and dash.\n";
@@ -447,15 +455,14 @@ END
                         "closed") .
                     ", send any further explanations to $data->{originator}";
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
-                   if ( length( $gDoneList ) > 0 and
-                        length( $gListDomain ) > 0 ) {
-                        add_recipients(recipients => \%recipients,
-                                       type => 'cc',
-                                       address => "$gDoneList\@$gListDomain",
-                                      );
-                   }
-                    $data->{done}= $replyto;
+                  $affected_packages{$data->{package}} = 1;
+                   add_recipients(data => $data,
+                                 recipients => \%recipients,
+                                 actions_taken => {done => 1},
+                                 transcript   => $transcript,
+                                 ($dl > 0 ? (debug => $transcript):()),
+                                );
+                   $data->{done}= $replyto;
                     my @keywords= split ' ', $data->{keywords};
                    my $extramessage = '';
                     if (grep $_ eq 'pending', @keywords) {
@@ -509,13 +516,22 @@ END
                 $action= "$gBug assigned to package \`$newpackage'.";
             }
             do {
-                add_recipients(data => $data, recipients => \%recipients);
+               $affected_packages{$data->{package}} = 1;
+                add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                 $data->{package}= $newpackage;
                 $data->{found_versions}= [];
                 $data->{fixed_versions}= [];
                 # TODO: what if $newpackage is a source package?
                 addfoundversions($data, $data->{package}, $version, 'binary');
-                add_recipients(data => $data, recipients => \%recipients);
+                add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
             } while (&getnextbug);
         }
     } elsif (m/^reopen\s+\#?(-?\d+)$/i ? ($noriginator='', 1) :
@@ -537,7 +553,12 @@ END
                     $noriginator eq '' ? "$gBug reopened, originator not changed." :
                         "$gBug reopened, originator set to $noriginator.";
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
+                   $affected_packages{$data->{package}} = 1;
+                    add_recipients(data => $data,
+                                  recipients => \%recipients,
+                                  transcript   => $transcript,
+                                  ($dl > 0 ? (debug => $transcript):()),
+                                 );
                     $data->{originator}= $noriginator eq '' ?  $data->{originator} : $noriginator;
                     $data->{fixed_versions}= [];
                     $data->{done}= '';
@@ -561,7 +582,12 @@ END
                         "$gBug marked as found in version $version." :
                         "$gBug reopened.";
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
+                    $affected_packages{$data->{package}} = 1;
+                   add_recipients(data => $data,
+                                  recipients => \%recipients,
+                                  transcript   => $transcript,
+                                  ($dl > 0 ? (debug => $transcript):()),
+                                 );
                     # The 'done' field gets a bit weird with version
                     # tracking, because a bug may be closed by multiple
                     # people in different branches. Until we have something
@@ -600,7 +626,12 @@ END
                 $extramessage= "(By the way, this $gBug is currently marked as done.)\n";
             }
             do {
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                 removefoundversions($data, $data->{package}, $version, 'binary');
             } while (&getnextbug);
        }
@@ -617,7 +648,12 @@ END
                       "$gBug marked as fixed in version $version." :
                            "$gBug reopened.";
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
+                    $affected_packages{$data->{package}} = 1;
+                   add_recipients(data => $data,
+                                  recipients => \%recipients,
+                                  transcript   => $transcript,
+                                  ($dl > 0 ? (debug => $transcript):()),
+                                 );
                     addfixedversions($data, $data->{package}, $version, 'binary');
               } while (&getnextbug);
        }
@@ -634,7 +670,12 @@ END
                       "$gBug no longer marked as fixed in version $version." :
                            "$gBug reopened.";
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
+                    $affected_packages{$data->{package}} = 1;
+                   add_recipients(data => $data,
+                                  recipients => \%recipients,
+                                  transcript   => $transcript,
+                                  ($dl > 0 ? (debug => $transcript):()),
+                                 );
                     removefixedversions($data, $data->{package}, $version, 'binary');
               } while (&getnextbug);
        }
@@ -654,7 +695,12 @@ END
         elsif (&getbug) {
             if (&checkpkglimit) {
                 &foundbug;
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                 $oldsubmitter= $data->{originator};
                 $data->{originator}= $newsubmitter;
                 $action= "Changed $gBug submitter from $oldsubmitter to $newsubmitter.";
@@ -714,14 +760,14 @@ END
                 $extramessage= "(By the way, this $gBug is currently marked as done.)\n";
             }
             do {
-                add_recipients(data => $data, recipients => \%recipients);
-               if (length($gForwardList)>0 && length($gListDomain)>0 ) {
-                    add_recipients(recipients => \%recipients,
-                                   type => 'cc',
-                                   address => "$gForwardList\@$gListDomain",
-                                  );
-               }
-                $data->{forwarded}= $whereto;
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              actions_taken => {forwarded => 1},
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
+               $data->{forwarded}= $whereto;
             } while (&getnextbug);
         }
     } elsif (m/^notforwarded\s+\#?(-?\d+)$/i) {
@@ -735,7 +781,12 @@ END
             } else {
     $action= "Removed annotation that $gBug had been forwarded to $data->{forwarded}.";
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
+                    $affected_packages{$data->{package}} = 1;
+                   add_recipients(data => $data,
+                                  recipients => \%recipients,
+                                  transcript   => $transcript,
+                                  ($dl > 0 ? (debug => $transcript):()),
+                                 );
                     $data->{forwarded}= '';
                 } while (&getnextbug);
             }
@@ -759,7 +810,12 @@ END
             $printseverity= "$gDefaultSeverity" if $printseverity eq '';
            $action= "Severity set to \`$newseverity' from \`$printseverity'";
            do {
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                 if (defined $gStrongList and isstrongseverity($newseverity)) {
                     addbcc("$gStrongList\@$gListDomain");
                 }
@@ -806,7 +862,12 @@ END
                $action= "Tags removed: " . join(", ", @okaytags);
            }
            do {
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                $data->{keywords} = '' if ($addsub eq "set");
                # Allow removing obsolete tags.
                if ($addsub eq "sub") {
@@ -828,7 +889,7 @@ END
        $ok++;
        my $bugnum = $2; my $blockers = $4;
        my $addsub = "add";
-       $addsub = "sub" if ($1 eq "un");
+       $addsub = "sub" if (defined $1 and $1 eq "un");
        if ($bugnum =~ m/^-\d+$/ && defined $clonebugs{$bugnum}) {
             $bugnum = $clonebugs{$bugnum};
        }
@@ -889,7 +950,12 @@ END
            my %removedblocks;
            my %addedblocks;
            do {
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                my @oldblockerlist = split ' ', $data->{blockedby};
                $data->{blockedby} = '' if ($addsub eq "set");
                foreach my $b (@okayblockers) {
@@ -938,7 +1004,12 @@ END
         if (&getbug) {
             if (&checkpkglimit) {
                 &foundbug;
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                my $oldtitle = $data->{subject};
                 $data->{subject}= $newtitle;
                 $action= "Changed $gBug title to `$newtitle' from `$oldtitle'.";
@@ -969,7 +1040,12 @@ END
                 my $discref= $ref;
                @bug_affected{@newmergelist} = 1 x @newmergelist;
                 do {
-                    add_recipients(data => $data, recipients => \%recipients);
+                    $affected_packages{$data->{package}} = 1;
+                   add_recipients(data => $data,
+                                  recipients => \%recipients,
+                                  transcript   => $transcript,
+                                  ($dl > 0 ? (debug => $transcript):()),
+                                 );
                    $data->{mergedwith}= ($ref == $discref) ? ''
                         : join(' ',grep($_ ne $ref,@newmergelist));
                 } while (&getnextbug);
@@ -1003,6 +1079,8 @@ END
            &checkmatch('blocked-by','m_blockedby',$data->{blockedby},@newmergelist);
            &checkmatch('done mark','m_done',length($data->{done}) ? 'done' : 'open',@newmergelist);
            &checkmatch('owner','m_owner',$data->{owner},@newmergelist);
+           &checkmatch('summary','m_summary',$data->{summary},@newmergelist);
+           &checkmatch('affects','m_affects',$data->{affects},@newmergelist);
            foreach my $t (split /\s+/, $data->{keywords}) { $tags{$t} = 1; }
            foreach my $f (@{$data->{found_versions}}) { $found{$f} = 1; }
            foreach my $f (@{$data->{fixed_versions}}) { $fixed{$f} = 1; }
@@ -1022,7 +1100,12 @@ END
            delete @fixed{keys %found};
            for $ref (@newmergelist) {
                &getbug || die "huh ?  $gBug $ref disappeared during merge";
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                @bug_affected{@newmergelist} = 1 x @newmergelist;
                $data->{mergedwith}= join(' ',grep($_ != $ref,@newmergelist));
                $data->{keywords}= join(' ', keys %tags);
@@ -1083,13 +1166,18 @@ END
            delete @fixed{keys %found};
            for $ref (@newmergelist) {
                &getbug || die "huh ?  $gBug $ref disappeared during merge";
-                add_recipients(data => $data, recipients => \%recipients);
+                $affected_packages{$data->{package}} = 1;
+               add_recipients(data => $data,
+                              recipients => \%recipients,
+                              transcript   => $transcript,
+                              ($dl > 0 ? (debug => $transcript):()),
+                             );
                @bug_affected{@newmergelist} = 1 x @newmergelist;
                $data->{mergedwith}= join(' ',grep($_ != $ref,@newmergelist));
                $data->{keywords}= join(' ', keys %tags);
                $data->{found_versions}= [sort keys %found];
                $data->{fixed_versions}= [sort keys %fixed];
-               my @field_list = qw(forwarded package severity blocks blockedby owner done);
+               my @field_list = qw(forwarded package severity blocks blockedby owner done affects summary);
                @{$data}{@field_list} = @{$master_bug_data}{@field_list};
                &savebug;
            }
@@ -1106,6 +1194,7 @@ END
        $ref = $origref;
        $bug_affected{$ref} = 1;
        if (&setbug) {
+           $affected_packages{$data->{package}} = 1;
            if (length($data->{mergedwith})) {
                print {$transcript} "$gBug is marked as being merged with others. Use an existing clone.\n\n";
                $errors++;
@@ -1171,9 +1260,57 @@ END
                %limit_pkgs = ();
                print {$transcript} "Not ignoring any bugs.\n\n";
        }
-    } elsif (m/^owner\s+\#?(-?\d+)\s+((?:\S.*\S)|\!)$/i) {
+    } elsif (m/^affects?\s+\#?(-?\d+)(?:\s+((?:[=+-])?)\s*(\S.*)?)?\s*$/i) {
+       $ok++;
+        $ref = $1;
+       my $add_remove = $2 || '';
+       my $packages = $3 || '';
+       $ref = $clonebugs{$ref} if exists $clonebugs{$ref};
+       $bug_affected{$ref} = 1;
+       eval {
+            affects(bug          => $ref,
+                    transcript   => $transcript,
+                    ($dl > 0 ? (debug => $transcript):()),
+                    requester    => $header{from},
+                    request_addr => $controlrequestaddr,
+                    message      => \@log,
+                    recipients   => \%recipients,
+                    packages     => [splitpackages($3)],
+                    ($add_remove eq '+'?(add => 1):()),
+                    ($add_remove eq '-'?(remove => 1):()),
+                   );
+       };
+       if ($@) {
+           $errors++;
+           print {$transcript} "Failed to give $ref a summary: $@";
+       }
+
+    } elsif (m/^summary\s+\#?(-?\d+)\s*(\d+|)\s*$/i) {
+       $ok++;
+        $ref = $1;
+       my $summary_msg = length($2)?$2:undef;
+       $ref = $clonebugs{$ref} if exists $clonebugs{$ref};
+       $bug_affected{$ref} = 1;
+       eval {
+           summary(bug          => $ref,
+                   transcript   => $transcript,
+                   ($dl > 0 ? (debug => $transcript):()),
+                   requester    => $header{from},
+                   request_addr => $controlrequestaddr,
+                   message      => \@log,
+                   recipients   => \%recipients,
+                   summary      => $summary_msg,
+                  );
+       };
+       if ($@) {
+           $errors++;
+           print {$transcript} "Failed to give $ref a summary: $@";
+       }
+
+    } elsif (m/^owner\s+\#?(-?\d+)\s+((?:\S.*\S)|\!)\s*$/i) {
        $ok++;
         $ref = $1;
+       $ref = $clonebugs{$ref} if exists $clonebugs{$ref};
        my $newowner = $2;
        if ($newowner eq '!') {
            $newowner = $replyto;
@@ -1190,9 +1327,14 @@ END
                  owner        => $newowner,
                 );
        };
-    } elsif (m/^noowner\s+\#?(-?\d+)$/i) {
+       if ($@) {
+           $errors++;
+           print {$transcript} "Failed to mark $ref as having an owner: $@";
+       }
+    } elsif (m/^noowner\s+\#?(-?\d+)\s*$/i) {
         $ok++;
         $ref = $1;
+       $ref = $clonebugs{$ref} if exists $clonebugs{$ref};
        $bug_affected{$ref} = 1;
        eval {
            owner(bug          => $ref,
@@ -1212,6 +1354,7 @@ END
     } elsif (m/^unarchive\s+#?(\d+)$/i) {
         $ok++;
         $ref = $1;
+        $ref = $clonebugs{$ref} if exists $clonebugs{$ref};
         $bug_affected{$ref} = 1;
         eval {
              bug_unarchive(bug        => $ref,
@@ -1230,6 +1373,7 @@ END
     } elsif (m/^archive\s+#?(\d+)$/i) {
         $ok++;
         $ref = $1;
+        $ref = $clonebugs{$ref} if exists $clonebugs{$ref};
         $bug_affected{$ref} = 1;
         eval {
              bug_archive(bug => $ref,
@@ -1266,45 +1410,18 @@ if (!$ok && !$quickabort) {
     print {$transcript} "\n";
 }
 
-print {$transcript} "MC\n" if $dl>1;
-my @maintccs= ();
-my @maintccaddrs = ();
-my %maintccreasons;
-for my $maint (keys %maintccreasons) {
-print {$transcript} "MM|$maint|\n" if $dl>1;
-    next if $maint eq $replyto;
-    my $reasonstring= '';
-    my $reasonsref= $maintccreasons{$maint};
-print {$transcript} "MY|$maint|\n" if $dl>2;
-    for my $p (sort keys %$reasonsref) {
-print {$transcript} "MP|$p|\n" if $dl>2;
-        $reasonstring.= ', ' if length($reasonstring);
-        $reasonstring.= $p.' ' if length($p);
-        $reasonstring.= join(' ',map("#$_",sort keys %{$$reasonsref{$p}}));
-    }
-    if (length($reasonstring) > 40) {
-       (substr $reasonstring, 37) = "...";
-    }
-    $reasonstring = "" if (!defined($reasonstring));
-    push(@maintccs,"$maint ($reasonstring)");
-    push(@maintccaddrs,"$maint");
-}
-
-my $maintccs = "";
-if (@maintccs) {
-    print {$transcript} "MC|".join(', ',@maintccs)."|\n" if $dl>2;
-    $maintccs .= "Cc: " . join(",\n    ",@maintccs) . "\n";
-}
+my @maintccs = determine_recipients(recipients => \%recipients,
+                                   address_only => 1,
+                                   cc => 1,
+                                  );
+my $maintccs = 'Cc: '.join(",\n    ",
+                   determine_recipients(recipients => \%recipients,
+                                        cc => 1,
+                                       )
+                  )."\n";
 
-my %packagepr;
-for my $maint (keys %maintccreasons) {
-     for my $package (keys %{$maintccreasons{$maint}}) {
-         next unless length $package;
-         $packagepr{$package} = 1;
-     }
-}
 my $packagepr = '';
-$packagepr = "X-${gProject}-PR-Package: " . join(keys %packagepr) . "\n" if keys %packagepr;
+$packagepr = "X-${gProject}-PR-Package: " . join(keys %affected_packages) . "\n" if keys %affected_packages;
 
 # Add Bcc's to subscribed bugs
 # now handled by Debbugs::Recipients
@@ -1335,7 +1452,12 @@ $gMaintainer
 (administrator, $gProject $gBugs database)
 END
 
-my $repliedshow= join(', ',$replyto,@maintccaddrs);
+my $repliedshow= join(', ',$replyto,
+                     determine_recipients(recipients => \%recipients,
+                                          cc => 1,
+                                          address_only => 1,
+                                         )
+                    );
 # -1 is the service.in log
 &filelock("lock/-1");
 open(AP,">>db-h/-1.log") || die "open db-h/-1.log: $!";
@@ -1351,7 +1473,13 @@ close(AP) || die "open db-h/-1.log: $!";
 &unfilelock;
 utime(time,time,"db-h");
 
-&sendmailmessage($reply,exists $header{'x-debbugs-no-ack'}?():$replyto,@maintccaddrs,@bcc);
+&sendmailmessage($reply,
+                exists $header{'x-debbugs-no-ack'}?():$replyto,
+                make_list(values %{{determine_recipients(recipients => \%recipients,
+                                                         address_only => 1,
+                                                        )}}
+                         ),
+               );
 
 unlink("incoming/P$nn") || die "unlinking incoming/P$nn: $!";
 
@@ -1404,20 +1532,23 @@ sub message_body_template{
 }
 
 sub sendhelp {
-        &sendtxthelpraw("bug-log-mailserver.txt","instructions for request\@$gEmailDomain");
-        &sendtxthelpraw("bug-maint-mailcontrol.txt","instructions for control\@$gEmailDomain")
-            if $control;
+     if ($control) {
+         &sendtxthelpraw("bug-maint-mailcontrol.txt","instructions for control\@$gEmailDomain")
+     }
+     else {
+         &sendtxthelpraw("bug-log-mailserver.txt","instructions for request\@$gEmailDomain");
+     }
 }
 
 #sub unimplemented {
 #    print {$transcript} "Sorry, command $_[0] not yet implemented.\n\n";
 #}
-
+our %checkmatch_values;
 sub checkmatch {
     my ($string,$mvarname,$svarvalue,@newmergelist) = @_;
     my ($mvarvalue);
     if (@newmergelist) {
-        eval "\$mvarvalue= \$$mvarname";
+       $mvarvalue = $checkmatch_values{$mvarname};
         print {$transcript} "D| checkmatch \`$string' /$mvarname/$mvarvalue/$svarvalue/\n"
             if $dl;
         $mismatch .=
@@ -1428,7 +1559,7 @@ sub checkmatch {
     } else {
         print {$transcript} "D| setupmatch \`$string' /$mvarname/$svarvalue/\n"
              if $dl;
-        eval "\$$mvarname= \$svarvalue";
+        $checkmatch_values{$mvarname} = $svarvalue;
     }
 }