]> git.donarmstrong.com Git - wannabuild.git/blob - bin/wanna-build
Restore sendmail setting.
[wannabuild.git] / bin / wanna-build
1 #!/usr/bin/perl
2
3 # wanna-build: coordination script for Debian buildds
4 # Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5 # Copyright (C) 2005-2008 Ryan Murray <rmurray@debian.org>
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; either version 2 of the
10 # License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 #
21
22 package conf;
23 # defaults
24 $basedir ||= "/var/lib/debbuild";
25 $dbbase ||= "build-db";
26 $transactlog ||= "transactions.log";
27 $mailprog ||= "/usr/sbin/sendmail";
28 require "/etc/wanna-build.conf";
29 die "$conf::basedir is not a directory\n" if ! -d $conf::basedir;
30 die "dbbase is empty\n" if ! $dbbase;
31 die "transactlog is empty\n" if ! $transactlog;
32 die "mailprog binary $conf::mailprog does not exist or isn't executable\n"
33         if !-x $conf::mailprog;
34 die "no distributions defined\n" if ! %distributions;
35 package main;
36
37 use strict;
38 use POSIX;
39 use FileHandle;
40 use File::Copy;
41 use DBI;
42 use WannaBuild;
43
44 our ($verbose, $mail_logs, $list_order, $list_state,
45     $curr_date, $op_mode, $user, $real_user, $distribution,
46     $fail_reason, $opt_override, $import_from, $opt_create_db,
47     %prioval, %sectval,
48     $info_all_dists, $arch,
49     $category, %catval, %short_category,
50     $short_date, $list_min_age, $dbbase, @curr_time,
51     $build_priority, %new_vers, $binNMUver, %merge_srcvers, %merge_binsrc);
52
53 # global vars
54 $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:/org/wanna-build/bin/";
55 $verbose = 0;
56 $mail_logs = "";
57 @curr_time = gmtime;
58 $curr_date = strftime("%Y %b %d %H:%M:%S",@curr_time);
59 $short_date = strftime("%m/%d/%y",@curr_time);
60 $| = 1;
61
62 # map program invocation names to operation modes
63 my %prognames = ( "uploaded-build"  => "set-uploaded",
64                                   "failed-build"    => "set-failed",
65                                   "no-build"            => "set-not-for-us",
66                                   "give-back-build" => "set-needs-build",
67                                   "dep-wait-build"  => "set-dep-wait",
68                                   "forget-build"        => "forget",
69                                   "merge-quinn"         => "merge-quinn",
70                                   "merge-packages"  => "merge-packages",
71                                   "merge-sources"   => "merge-sources",
72                                   "build-info"          => "info" );
73
74 %short_category = ( u => "uploaded-fixed-pkg",
75                                     f => "fix-expected",
76                                         r => "reminder-sent",
77                                         n => "nmu-offered",
78                                         e => "easy",
79                                         m => "medium",
80                                         h => "hard",
81                                         c => "compiler-error",
82                                         "" => "none" );
83
84 my $progname;
85 ($progname = $0) =~ s,.*/,,;
86 if ($prognames{$progname}) {
87         $op_mode = $prognames{$progname};
88 }
89 elsif ($progname =~ /^list-(.*)$/) {
90         $op_mode = "list";
91         $list_state = ($1 eq "all") ? "" : $1;
92 }
93
94 my %options =
95         (# flags
96          verbose        => { short => "v", flag => \$verbose },
97          override               => { short => "o", flag => \$opt_override },
98          "create-db"    => { flag => \$opt_create_db },
99          "correct-compare" => { flag => \$WannaBuild::opt_correct_version_cmp },
100          # TODO: remove after buildds no longer pass to wanna-build
101          "no-propagation" => { short => "N" },
102          "no-down-propagation" => { short => "D" },
103          # normal actions
104          take                   => { mode => "set-building" },
105          failed                 => { short => "f", mode => "set-failed" },
106          uploaded               => { short => "u", mode => "set-uploaded" },
107          "no-build"             => { short => "n", mode => "set-not-for-us" },
108          built                  => { mode => "set-built" },
109          attempted              => { mode => "set-attempted" },
110          "give-back"            => { mode => "set-needs-build" },
111          "dep-wait"             => { mode => "set-dep-wait" },
112          forget                 => { mode => "forget" },
113          'forget-user' => { mode => 'forget-user' },
114          "merge-quinn"  => { mode => "merge-quinn" },
115          "merge-partial-quinn" => { mode => "merge-partial-quinn" },
116          "merge-packages" => { mode => "merge-packages" },
117          "merge-sources" => { mode => "merge-sources" },
118          "pretend-avail" => { short => "p", mode => "pretend-avail" },
119          "merge-all"     => { mode => "merge-all" },
120          info                   => { short => "i", mode => "info" },
121          'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver, 
122                              code => sub { die "Invalid binNMU version: $binNMUver\n"
123                                 if $binNMUver !~ /^([\d]*)$/ and $1 >= 0; } },
124          'perm-build-priority'    => { mode => "set-permanent-build-priority", arg => \$build_priority,
125                               code => sub { die "Invalid build priority: $build_priority\n"
126                                 if $build_priority !~ /^-?[\d]+$/; } },
127          'build-priority'    => { mode => "set-build-priority", arg => \$build_priority,
128                               code => sub { die "Invalid build priority: $build_priority\n"
129                                 if $build_priority !~ /^-?[\d]+$/; } },
130          list                   =>
131          { short => "l", mode => "list", arg => \$list_state,
132            code => sub {
133                    die "Unknown state to list: $list_state\n"
134                            if !isin( $list_state, qw(needs-build building uploaded
135                                                  built build-attempted failed installed dep-wait
136                                                  not-for-us all failed-removed
137                                                  install-wait reupload-wait bd-uninstallable));} },
138          # options with args
139          dist           =>
140          { short => "d", arg => \$distribution,
141            code => sub {
142                    if ($distribution eq "a" || $distribution eq "all") {
143                            $info_all_dists = 1;
144                            $distribution = "";
145                    }
146                    else {
147                            $distribution = "oldstable"   if $distribution eq "o";
148                            $distribution = "stable"   if $distribution eq "s";
149                            $distribution = "testing"  if $distribution eq "t";
150                            $distribution = "unstable" if $distribution eq "u";
151                    }
152            } },
153          order          =>
154          { short => "O", arg => \$list_order,
155            code => sub {
156                    die "Bad ordering character\n"
157                            if $list_order !~ /^[PSpsncb]+$/;
158            } },
159          message        => { short => "m", arg => \$fail_reason },
160          # database is deprecated, use arch instead.
161          database       => { short => "b", arg => \$conf::dbbase },
162          arch           => { short => "A", arg => \$arch },
163          user           => { short => "U", arg => \$user },
164          category               => { short => "c", arg => \$category,
165                                                  code => sub {
166                                                          $category = $short_category{$category}
167                                                                  if exists $short_category{$category};
168                                                          die "Unknown category: $category\n"
169                                                                  if !isin( $category, values %short_category );
170                                                  } },
171          "min-age"      => { short => "a", arg => \$list_min_age,
172                                                  code => sub {
173                                                          die "Argument of --min-age must be a non-zero number\n"
174                                                                  if $list_min_age == 0;
175                                                          $list_min_age *= 24*60*60;
176                                                  } },
177          "max-age"      => { arg => \$list_min_age,
178                                                  code => sub {
179                                                          die "Argument of --max-age must be a non-zero number\n"
180                                                                  if $list_min_age == 0;
181                                                          $list_min_age *= -24*60*60;
182                                                  } },
183          # special actions
184          import         => { arg => \$import_from, mode => "import" },
185          "manual-edit"  => { mode => "manual-edit" },
186          );
187
188 while( @ARGV && $ARGV[0] =~ /^-/ ) {
189         $_ = shift @ARGV;
190         last if $_ eq "--";
191         my($opt, $optname, $arg);
192         if (/^--([^=]+)(=|$)/) {
193                 $optname = $1;
194                 $opt = $options{$optname};
195                 $arg = $1 if /^--\Q$optname\E=((.|\n)*)$/;
196         }
197         else {
198                 $optname = substr( $_, 1, 1 );
199                 $opt = (grep { defined($_->{short}) ? $_->{short} eq $optname : 0} values %options)[0];
200                 $arg = $1 if /^-$optname(.+)$/;
201         }
202         if (!$opt) {
203                 warn "Unknown option: --$1\n";
204                 usage();
205         }
206         if ($opt->{arg}) {
207                 if (!defined $arg) {
208                         die "$optname option missing argument\n" if !@ARGV;
209                         $arg = shift @ARGV;
210                 }
211                 ${$opt->{arg}} = $arg;
212         }
213         elsif (defined $arg) {
214                 die "Option $optname takes no argument\n";
215         }
216         
217         if ($opt->{mode}) {
218                 die "Conflicting operation modes\n" if $op_mode;
219                 $op_mode = $opt->{mode};
220         }
221         if ($opt->{flag}) {
222                 ${$opt->{flag}}++;
223         }
224         if ($opt->{code}) {
225                 &{$opt->{code}};
226         }
227 }
228
229 $op_mode = $category ? "set-failed" : "set-building"
230         if !$op_mode; # default operation
231 $list_order = $list_state eq "failed" ? 'fPcpsn' : 'PScpsn'
232         if !$list_order and $list_state;
233 $distribution ||= "unstable";
234 die "Bad distribution '$distribution'\n"
235         if !isin($distribution, keys %conf::distributions);
236
237 # If they didn't specify an arch, try to get it from database name which
238 # is in the form of $arch/build-db
239 # This is for backwards compatibity with older versions that didn't
240 # specify the arch yet.
241 $conf::dbbase =~ m#^([^/]+)#;
242 $arch ||= $1;
243
244 # TODO: Check that it's an known arch (for that dist), and give
245 # a proper error.
246
247 if ($verbose) {
248         my $version = '$Revision: db181a534e9d $ $Date: 2008/03/26 06:20:22 $ $Author: rmurray $';
249         $version =~ s/(^\$| \$ .*$)//g;
250         print "wanna-build $version for $distribution on $arch\n";
251 }
252
253 if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import 
254                                   merge-packages manual-edit
255                                   merge-sources))) {
256         warn "No packages given.\n";
257         usage();
258 }
259
260 $real_user = (getpwuid($<))[0];
261 die "Can't determine your user name\n"
262         if $op_mode ne "list" && !$user &&
263            !($user = $real_user);
264
265 if (!$fail_reason) {
266         if ($op_mode eq "set-failed" && !$category) {
267                 print "Enter reason for failing (end with '.' alone on ".
268                       "its line):\n";
269                 my $line;
270                 while(!eof(STDIN)) {
271                         $line = <STDIN>;
272                         last if $line eq ".\n";
273                         $fail_reason .= $line;
274                 }
275                 chomp( $fail_reason );
276         } elsif ($op_mode eq "set-dep-wait") {
277                 print "Enter dependencies (one line):\n";
278                 my $line;
279                 while( !$line && !eof(STDIN) ) {
280                         chomp( $line = <STDIN> );
281                 }
282                 die "No dependencies given\n" if !$line;
283                 $fail_reason = $line;
284         } elsif ($op_mode eq "set-binary-nmu" and $binNMUver > 0) {
285                 print "Enter changelog entry (one line):\n";
286                 my $line;
287                 while( !$line && !eof(STDIN) ) {
288                         chomp( $line = <STDIN> );
289                 }
290                 die "No changelog entry given\n" if !$line;
291                 $fail_reason = $line;
292         }
293 }
294
295 my $dbh;
296
297 END {
298         if (defined $dbh)
299         {
300                 $dbh->disconnect or warn $dbh->errstr;
301         }
302 }
303
304 $dbh = DBI->connect("DBI:Pg:database=wanna-build") || 
305         die "FATAL: Cannot open database: $DBI::errstr\n";
306
307 # TODO: This shouldn't be needed, file a bug.
308 $dbh->{pg_server_prepare} = 0;
309
310 $dbh->begin_work or die $dbh->errstr;
311
312 process();
313
314 $dbh->commit;
315 $dbh->disconnect;
316
317 if ($mail_logs && $conf::log_mail) {
318         send_mail( $conf::log_mail,
319                            "wanna-build $distribution state changes $curr_date",
320                            "State changes at $curr_date for distribution ".
321                            "$distribution:\n\n$mail_logs\n" );
322 }
323
324 exit 0;
325
326
327 sub process {
328
329         SWITCH: foreach ($op_mode) {
330                 /^set-(.+)/ && do {
331                         add_packages( $1, @ARGV );
332                         last SWITCH;
333                 };
334                 /^list/ && do {
335                         list_packages( $list_state );
336                         last SWITCH;
337                 };
338                 /^info/ && do {
339                         info_packages( @ARGV );
340                         last SWITCH;
341                 };
342                 /^forget-user/ && do {
343                         die "This operation is restricted to admin users\n"
344                                 if (defined @conf::admin_users and
345                                     !isin( $real_user, @conf::admin_users));
346                         forget_users( @ARGV );
347                         last SWITCH;
348                 };
349                 /^forget/ && do {
350                         forget_packages( @ARGV );
351                         last SWITCH;
352                 };
353                 /^merge-partial-quinn/ && do {
354                         die "This operation is restricted to admin users\n"
355                                 if (defined @conf::admin_users and
356                                     !isin( $real_user, @conf::admin_users));
357                         parse_quinn_diff(1);
358                         last SWITCH;
359                 };
360                 /^merge-quinn/ && do {
361                         die "This operation is restricted to admin users\n"
362                                 if (defined @conf::admin_users and
363                                     !isin( $real_user, @conf::admin_users));
364                         parse_quinn_diff(0);
365                         last SWITCH;
366                 };
367                 /^merge-packages/ && do {
368                         die "This operation is restricted to admin users\n"
369                                 if (defined @conf::admin_users and
370                                     !isin( $real_user, @conf::admin_users));
371                         parse_packages();
372                         last SWITCH;
373                 };
374                 /^merge-sources/ && do {
375                         die "This operation is restricted to admin users\n"
376                                 if (defined @conf::admin_users and
377                                     !isin( $real_user, @conf::admin_users));
378                         parse_sources(0);
379                         last SWITCH;
380                 };
381                 /^pretend-avail/ && do {
382                         pretend_avail( @ARGV );
383                         last SWITCH;
384                 };
385                 /^merge-all/ && do {
386                         die "This operation is restricted to admin users\n"
387                                 if (defined @conf::admin_users and
388                                     !isin( $real_user, @conf::admin_users));
389                         my @ARGS = @ARGV;
390                         @ARGV = ( $ARGS[0] );
391                         my $pkgs = parse_packages();
392                         @ARGV = ( $ARGS[1] );
393                         parse_quinn_diff(0);
394                         @ARGV = ( $ARGS[2] );
395                         my $srcs = parse_sources(1);
396                         call_edos_depcheck( $ARGS[0], $srcs );
397                         last SWITCH;
398                 };
399                 /^import/ && do {
400                         die "This operation is restricted to admin users\n"
401                                 if (defined @conf::admin_users and
402                                     !isin( $real_user, @conf::admin_users));
403                         $dbh->do("DELETE from " . table_name() . 
404                                 " WHERE distribution = ?", undef,
405                                 $distribution)
406                                 or die $dbh->errstr;
407                         forget_users();
408                         read_db( $import_from );
409                         last SWITCH;
410                 };
411
412                 die "Unexpected operation mode $op_mode\n";
413         }
414         if (not -t and $user =~ /-/) {
415                 my $userinfo = get_user_info($user);
416                 if (!defined $userinfo)
417                 {
418                         add_user_info($user);
419                 }
420                 else
421                 {
422                         update_user_info($user);
423                 }
424         }
425 }
426
427 sub add_packages {
428         my $newstate = shift;
429         my( $package, $name, $version, $ok, $reason );
430         
431         foreach $package (@_) {
432                 $package =~ s,^.*/,,; # strip path
433                 $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
434                 $package =~ s/_[a-zA-Z\d-]+\.changes$//; # strip extension
435                 if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
436                         ($name,$version) = ($1,$2);
437                 }
438                 else {
439                         warn "$package: can't extract package name and version ".
440                                  "(bad format)\n";
441                         next;
442                 }
443
444                 if ($op_mode eq "set-building") {
445                         add_one_building( $name, $version );
446                 }
447                 elsif ($op_mode eq "set-built") {
448                         add_one_built( $name, $version );
449                 }
450                 elsif ($op_mode eq "set-attempted") {
451                         add_one_attempted( $name, $version );
452                 }
453                 elsif ($op_mode eq "set-uploaded") {
454                         add_one_uploaded( $name, $version );
455                 }
456                 elsif ($op_mode eq "set-failed") {
457                         add_one_failed( $name, $version );
458                 }
459                 elsif ($op_mode eq "set-not-for-us") {
460                         add_one_notforus( $name, $version );
461                 }
462                 elsif ($op_mode eq "set-needs-build") {
463                         add_one_needsbuild( $name, $version );
464                 }
465                 elsif ($op_mode eq "set-dep-wait") {
466                         add_one_depwait( $name, $version );
467                 }
468                 elsif ($op_mode eq "set-build-priority") {
469                         set_one_buildpri( $name, $version, 'buildpri' );
470                 }
471                 elsif ($op_mode eq "set-permanent-build-priority") {
472                         set_one_buildpri( $name, $version, 'permbuildpri' );
473                 }
474                 elsif ($op_mode eq "set-binary-nmu") {
475                         set_one_binnmu( $name, $version );
476                 }
477         }
478 }
479
480 sub add_one_building {
481         my $name = shift;
482         my $version = shift;
483         my( $ok, $reason );
484
485         $ok = 1;
486         my $pkg = get_source_info($name);
487         if (defined($pkg)) {
488                 if ($pkg->{'state'} eq "Not-For-Us") {
489                         $ok = 0;
490                         $reason = "not suitable for this architecture";
491                 }
492                 elsif ($pkg->{'state'} =~ /^Dep-Wait/) {
493                         $ok = 0;
494                         $reason = "not all source dependencies available yet";
495                 }
496                 elsif ($pkg->{'state'} =~ /^BD-Uninstallable/) {
497                         $ok = 0;
498                         $reason = "source dependencies are not installable";
499                 }
500                 elsif ($pkg->{'state'} eq "Uploaded" &&
501                            (version_lesseq($version, $pkg->{'version'}))) {
502                         $ok = 0;
503                         $reason = "already uploaded by $pkg->{'builder'}";
504                         $reason .= " (in newer version $pkg->{'version'})"
505                                 if !version_eq($pkg, $version);
506                 }
507                 elsif ($pkg->{'state'} eq "Installed" &&
508                            version_less($version,$pkg->{'version'})) {
509                         if ($opt_override) {
510                                 print "$name: Warning: newer version $pkg->{'version'} ".
511                                           "already installed, but overridden.\n";
512                         }
513                         else {
514                                 $ok = 0;
515                                 $reason = "newer version $pkg->{'version'} already in ".
516                                                   "archive; doesn't need rebuilding";
517                                 print "$name: Note: If the following is due to an epoch ",
518                                           " change, use --override\n";
519                         }
520                 }
521                 elsif ($pkg->{'state'} eq "Installed" &&
522                            pkg_version_eq($pkg,$version)) {
523                         $ok = 0;
524                         $reason = "is up-to-date in the archive; doesn't need rebuilding";
525                 }
526                 elsif ($pkg->{'state'} eq "Needs-Build" &&
527                            version_less($version,$pkg->{'version'})) {
528                         if ($opt_override) {
529                                 print "$name: Warning: newer version $pkg->{'version'} ".
530                                           "needs building, but overridden.";
531                         }
532                         else {
533                                 $ok = 0;
534                                 $reason = "newer version $pkg->{'version'} needs building, ".
535                                                   "not $version";
536                         }
537                 }
538                 elsif (isin($pkg->{'state'},qw(Building Built Build-Attempted))) {
539                         if (version_less($pkg->{'version'},$version)) {
540                                 print "$name: Warning: Older version $pkg->{'version'} ",
541                                       "is being built by $pkg->{'builder'}\n";
542                                 if ($pkg->{'builder'} ne $user) {
543                                         send_mail( $pkg->{'builder'},
544                                                            "package takeover in newer version",
545                                                            "You are building package '$name' in ".
546                                                            "version $version\n".
547                                                            "(as far as I'm informed).\n".
548                                                            "$user now has taken the newer ".
549                                                            "version $version for building.".
550                                                            "You can abort the build if you like.\n" );
551                                 }
552                         }
553                         else {
554                                 if ($opt_override) {
555                                         print "User $pkg->{'builder'} had already ",
556                                               "taken the following package,\n",
557                                                   "but overriding this as you request:\n";
558                                         send_mail( $pkg->{'builder'}, "package takeover",
559                                                            "The package '$name' (version $version) that ".
560                                                            "was taken by you\n".
561                                                            "has been taken over by $user\n" );
562                                 }
563                                 elsif ($pkg->{'builder'} eq $user) {
564                                         print "$name: Note: already taken by you.\n";
565                                         print "$name: ok\n" if $verbose;
566                                         return;
567                                 }
568                                 else {
569                                         $ok = 0;
570                                         $reason = "already taken by $pkg->{'builder'}";
571                                         $reason .= " (in newer version $pkg->{'version'})"
572                                                 if !version_eq($pkg->{'version'}, $version);
573                                 }
574                         }
575                 }
576                 elsif ($pkg->{'state'} =~ /^Failed/ &&
577                            pkg_version_eq($pkg, $version)) {
578                         if ($opt_override) {
579                                 print "The following package previously failed ",
580                                           "(by $pkg->{'builder'})\n",
581                                           "but overriding this as you request:\n";
582                                 send_mail( $pkg->{'builder'}, "failed package takeover",
583                                                    "The package '$name' (version $version) that ".
584                                                    "is taken by you\n".
585                                                    "and has failed previously has been taken over ".
586                                                    "by $user\n" )
587                                         if $pkg->{'builder'} ne $user;
588                         }
589                         else {
590                                 $ok = 0;
591                                 $reason = "build of $version failed previously:\n    ";
592                                 $reason .= join( "\n    ", split( "\n", $pkg->{'failed'} ));
593                                 $reason .= "\nalso the package doesn't need builing"
594                                         if $pkg->{'state'} eq 'Failed-Removed';
595                         }
596                 }
597         }
598         if ($ok) {
599                 my $ok = 'ok';
600                 if ($pkg->{'binary_nmu_version'}) {
601                         print "$name: Warning: needs binary NMU $pkg->{'binary_nmu_version'}\n" .
602                               "$pkg->{'binary_nmu_changelog'}\n";
603                         $ok = 'aok';
604                 } else {
605                         print "$name: Warning: Previous version failed!\n"
606                                 if $pkg->{'previous_state'} =~ /^Failed/ ||
607                                    $pkg->{'state'} =~ /^Failed/;
608                 }
609                 change_state( \$pkg, 'Building' );
610                 $pkg->{'package'} = $name;
611                 $pkg->{'version'} = $version;
612                 $pkg->{'builder'} = $user;
613                 log_ta( $pkg, "--take" );
614                 update_source_info($pkg);
615                 print "$name: $ok\n" if $verbose;
616         }
617         else {
618                 print "$name: NOT OK!\n  $reason\n";
619         }
620 }
621
622 sub add_one_attempted {
623         my $name = shift;
624         my $version = shift;
625         my $pkg = get_source_info($name);
626
627         if (!defined($pkg)) {
628                 print "$name: not registered yet.\n";
629                 return;
630         }
631
632         if ($pkg->{'state'} ne "Building" ) {
633                 print "$name: not taken for building (state is $pkg->{'state'}). ",
634                           "Skipping.\n";
635                 return;
636         }
637         if ($pkg->{'builder'} ne $user) {
638                 print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
639                 return;
640         }
641         elsif ( !pkg_version_eq($pkg, $version) ) {
642                 print "$name: version mismatch ".
643                           "$(pkg->{'version'} ".
644                           "by $pkg->{'builder'})\n";
645                 return;
646         }
647
648         change_state( \$pkg, 'Build-Attempted' );
649         log_ta( $pkg, "--attempted" );
650         update_source_info($pkg);
651         print "$name: registered as uploaded\n" if $verbose;
652 }
653
654 sub add_one_built {
655         my $name = shift;
656         my $version = shift;
657         my $pkg = get_source_info($name);
658
659         if (!defined($pkg)) {
660                 print "$name: not registered yet.\n";
661                 return;
662         }
663
664         if ($pkg->{'state'} ne "Building" ) {
665                 print "$name: not taken for building (state is $pkg->{'state'}). ",
666                           "Skipping.\n";
667                 return;
668         }
669         if ($pkg->{'builder'} ne $user) {
670                 print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
671                 return;
672         }
673         elsif ( !pkg_version_eq($pkg, $version) ) {
674                 print "$name: version mismatch ".
675                           "$(pkg->{'version'} ".
676                           "by $pkg->{'builder'})\n";
677                 return;
678         }
679         change_state( \$pkg, 'Built' );
680         log_ta( $pkg, "--built" );
681         update_source_info($pkg);
682         print "$name: registered as built\n" if $verbose;
683 }
684
685 sub add_one_uploaded {
686         my $name = shift;
687         my $version = shift;
688         my $pkg = get_source_info($name);
689
690         if (!defined($pkg)) {
691                 print "$name: not registered yet.\n";
692                 return;
693         }
694
695         if ($pkg->{'state'} eq "Uploaded" &&
696                 pkg_version_eq($pkg,$version)) {
697                 print "$name: already uploaded\n";
698                 return;
699         }
700         if (!isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
701                 print "$name: not taken for building (state is $pkg->{'state'}). ",
702                           "Skipping.\n";
703                 return;
704         }
705         if ($pkg->{'builder'} ne $user) {
706                 print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
707                 return;
708         }
709         # strip epoch -- buildd-uploader used to go based on the filename.
710         # (to remove at some point)
711         my $pkgver;
712         ($pkgver = $pkg->{'version'}) =~ s/^\d+://;
713         $version =~ s/^\d+://; # for command line use
714         if ($pkg->{'binary_nmu_version'} ) {
715                 my $nmuver = binNMU_version($pkgver, $pkg->{'binary_nmu_version'});
716                 if (!version_eq( $nmuver, $version )) {
717                         print "$name: version mismatch ($nmuver registered). ",
718                                   "Skipping.\n";
719                         return;
720                 }
721         } elsif (!version_eq($pkgver, $version)) {
722                 print "$name: version mismatch ($pkg->{'version'} registered). ",
723                           "Skipping.\n";
724                 return;
725         }
726
727         change_state( \$pkg, 'Uploaded' );
728         log_ta( $pkg, "--uploaded" );
729         update_source_info($pkg);
730         print "$name: registered as uploaded\n" if $verbose;
731 }
732
733 sub add_one_failed {
734         my $name = shift;
735         my $version = shift;
736         my ($state, $cat);
737         my $pkg = get_source_info($name);
738
739         if (!defined($pkg)) {
740                 print "$name: not registered yet.\n";
741                 return;
742         }
743         $state = $pkg->{'state'};
744
745         if ($state eq "Not-For-Us") {
746                 print "$name: not suitable for this architecture anyway. Skipping.\n";
747                 return;
748         }
749         elsif ($state eq "Failed-Removed") {
750                 print "$name: failed previously and doesn't need building. Skipping.\n";
751                 return;
752         }
753         elsif ($state eq "Installed") {
754                 print "$name: Is already installed in archive. Skipping.\n";
755                 return;
756         }
757         elsif ($pkg->{'builder'} &&
758                    (($user ne $pkg->{'builder'}) &&
759                     !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user))) {
760                 print "$name: not taken by you, but by ".
761                           "$pkg->{'builder'}. Skipping.\n";
762                 return;
763         }
764         elsif ( !pkg_version_eq($pkg, $version) ) {
765                 print "$name: version mismatch ".
766                           "$(pkg->{'version'} ".
767                           "by $pkg->{'builder'})\n";
768                 return;
769         }
770
771         $cat = $category;
772         if (!$cat && $fail_reason =~ /^\[([^\]]+)\]/) {
773                 $cat = $1;
774                 $cat = $short_category{$cat} if exists $short_category{$cat};
775                 if (!isin( $cat, values %short_category )) {
776                         print "$name: Warning: unknown category $cat; discarded\n";
777                         $cat = "";
778                 }
779                 $fail_reason =~ s/^\[[^\]]+\][ \t]*\n*//;
780         }
781
782         if ($state eq "Needs-Build") {
783                 print "$name: Warning: not registered for building previously, ".
784                           "but processing anyway.\n";
785         }
786         elsif ($state eq "Uploaded") {
787                 print "$name: Warning: marked as uploaded previously, ".
788                           "but processing anyway.\n";
789         }
790         elsif ($state eq "Dep-Wait") {
791                 print "$name: Warning: marked as waiting for dependencies, ".
792                           "but processing anyway.\n";
793         }
794         elsif ($state eq "BD-Uninstallable") {
795                 print "$name: Warning: marked as having uninstallable build-dependencies, ".
796                           "but processing anyway.\n";
797         }
798         elsif ($state eq "Failed") {
799                 print "$name: already registered as failed; will append new message\n"
800                         if $fail_reason;
801                 print "$name: already registered as failed; changing category\n"
802                         if $cat;
803         }
804
805         if (($cat eq "reminder-sent" || $cat eq "nmu-offered") &&
806                 defined $pkg->{'failed_category'} &&
807                 $pkg->{'failed_category'} ne $cat) {
808                 (my $action = $cat) =~ s/-/ /;
809                 $fail_reason .= "\n$short_date: $action";
810         }
811
812         change_state( \$pkg, 'Failed' );
813         $pkg->{'builder'} = $user;
814         $pkg->{'failed'} .= "\n" if $pkg->{'failed'};
815         $pkg->{'failed'} .= $fail_reason;
816         $pkg->{'failed_category'} = $cat if $cat;
817         if (defined $pkg->{'permbuildpri'}) {
818                 $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
819         } else {
820                 delete $pkg->{'buildpri'};
821         }
822         log_ta( $pkg, "--failed" );
823         update_source_info($pkg);
824         print "$name: registered as failed\n" if $verbose;
825 }
826
827 sub add_one_notforus {
828         my $name = shift;
829         my $version = shift;
830         my $pkg = get_source_info($name);
831
832         if ($pkg->{'state'} eq 'Not-For-Us') {
833                 # reset Not-For-Us state in case it's called twice; this is
834                 # the only way to get a package out of this state...
835                 # There is no really good state in which such packages should
836                 # be put :-( So use Failed for now.
837                 change_state( \$pkg, 'Failed' );
838                 $pkg->{'package'} = $name;
839                 $pkg->{'failed'} = "Was Not-For-Us previously";
840                 delete $pkg->{'builder'};
841                 delete $pkg->{'depends'};
842                 log_ta( $pkg, "--no-build(rev)" );
843                 print "$name: now not unsuitable anymore\n";
844
845                 send_mail( $conf::notforus_maint,
846                                    "$name moved out of Not-For-Us state",
847                                    "The package '$name' has been moved out of the Not-For-Us ".
848                                    "state by $user.\n".
849                                    "It should probably also be removed from ".
850                                    "Packages-arch-specific or\n".
851                                    "the action was wrong.\n" )
852                         if $conf::notforus_maint;
853         }
854         else {
855                 change_state( \$pkg, 'Not-For-Us' );
856                 $pkg->{'package'} = $name;
857                 delete $pkg->{'builder'};
858                 delete $pkg->{'depends'};
859                 delete $pkg->{'buildpri'};
860                 delete $pkg->{'binary_nmu_version'};
861                 delete $pkg->{'binary_nmu_changelog'};
862                 log_ta( $pkg, "--no-build" );
863                 print "$name: registered as unsuitable\n" if $verbose;
864
865                 send_mail( $conf::notforus_maint,
866                                    "$name set to Not-For-Us",
867                                    "The package '$name' has been set to state Not-For-Us ".
868                                    "by $user.\n".
869                                    "It should probably also be added to ".
870                                    "Packages-arch-specific or\n".
871                                    "the Not-For-Us state is wrong.\n" )
872                         if $conf::notforus_maint;
873         }
874         update_source_info($pkg);
875 }
876
877 sub add_one_needsbuild {
878         my $name = shift;
879         my $version = shift;
880         my $state;
881         my $pkg = get_source_info($name);
882
883         if (!defined($pkg)) {
884                 print "$name: not registered; can't give back.\n";
885                 return;
886         }
887         $state = $pkg->{'state'};
888
889         if ($state eq "BD-Uninstallable") {
890                 if ($opt_override) {
891                         print "$name: Forcing uninstallability mark to be removed. This is not permanent and might be reset with the next trigger run\n";
892
893                         change_state( \$pkg, 'Needs-Build' );
894                         delete $pkg->{'builder'};
895                         delete $pkg->{'depends'};
896                         log_ta( $pkg, "--give-back" );
897                         update_source_info($pkg);
898                         print "$name: given back\n" if $verbose;
899                         return;
900                 }
901                 else {
902                         print "$name: has uninstallable build-dependencies. Skipping\n",
903                                   "  (use --override to clear dependency list and ",
904                                   "give back anyway)\n";
905                         return;
906                 }
907         }
908         elsif ($state eq "Dep-Wait") {
909                 if ($opt_override) {
910                         print "$name: Forcing source dependency list to be cleared\n";
911                 }
912                 else {
913                         print "$name: waiting for source dependencies. Skipping\n",
914                                   "  (use --override to clear dependency list and ",
915                                   "give back anyway)\n";
916                         return;
917                 }
918         }
919         elsif (!isin( $state, qw(Building Built Build-Attempted))) {
920                 print "$name: not taken for building (state is $state).";
921                 if ($opt_override) {
922                         print "\n$name: Forcing give-back\n";
923                 }
924                 else {
925                         print " Skipping.\n";
926                         return;
927                 }
928         }
929         if (defined ($pkg->{'builder'}) && $user ne $pkg->{'builder'} &&
930                     !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user)) {
931                 print "$name: not taken by you, but by ".
932                           "$pkg->{'builder'}. Skipping.\n";
933                 return;
934         }
935         if (!pkg_version_eq($pkg, $version)) {
936                 print "$name: version mismatch ($pkg->{'version'} registered). ",
937                           "Skipping.\n";
938                 return;
939         }
940         if ($distribution eq "unstable") {
941                 change_state( \$pkg, 'BD-Uninstallable' );
942                 $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
943         } else {
944                 change_state( \$pkg, 'Needs-Build' );
945         }
946         $pkg->{'builder'} = undef;
947         $pkg->{'depends'} = undef;
948         log_ta( $pkg, "--give-back" );
949         update_source_info($pkg);
950         print "$name: given back\n" if $verbose;
951 }
952
953 sub set_one_binnmu {
954         my $name = shift;
955         my $version = shift;
956         my $pkg = get_source_info($name);
957         my $state;
958
959         if (!defined($pkg)) {
960                 print "$name: not registered; can't register for binNMU.\n";
961                 return;
962         }
963         my $db_ver = $pkg->{'version'};
964
965         if (!version_eq($db_ver, $version)) {
966                 print "$name: version mismatch ($db_ver registered). ",
967                           "Skipping.\n";
968                 return;
969         }
970         $state = $pkg->{'state'};
971
972         if (defined $pkg->{'binary_nmu_version'}) {
973                 if ($binNMUver == 0) {
974                         change_state( \$pkg, 'Installed' );
975                         delete $pkg->{'builder'};
976                         delete $pkg->{'depends'};
977                         delete $pkg->{'binary_nmu_version'};
978                         delete $pkg->{'binary_nmu_changelog'};
979                 } elsif ($binNMUver <= $pkg->{'binary_nmu_version'}) {
980                         print "$name: already building binNMU $pkg->{'binary_nmu_version'}\n";
981                         return;
982                 } else {
983                         $pkg->{'binary_nmu_version'} = $binNMUver;
984                         $pkg->{'binary_nmu_changelog'} = $fail_reason;
985                         $pkg->{'notes'} = 'out-of-date';
986                         $pkg->{'buildpri'} = $pkg->{'permbuildpri'}
987                                 if (defined $pkg->{'permbuildpri'});
988                 }
989                 log_ta( $pkg, "--binNMU" );
990                 update_source_info($pkg);
991                 return;
992         } elsif ($binNMUver == 0) {
993                 print "${name}_$version: no scheduled binNMU to cancel.\n";
994                 return;
995         }
996
997         if ($state ne 'Installed') {
998                 print "${name}_$version: not installed; can't register for binNMU.\n";
999                 return;
1000         }
1001
1002         my $fullver = binNMU_version($version,$binNMUver);
1003         if ( version_lesseq( $fullver, $pkg->{'installed_version'} ) )
1004         {
1005                 print "$name: binNMU $fullver is not newer than current version $pkg->{'installed_version'}\n";
1006                 return;
1007         }
1008
1009         change_state( \$pkg, 'BD-Uninstallable' );
1010         $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
1011         delete $pkg->{'builder'};
1012         delete $pkg->{'depends'};
1013         $pkg->{'binary_nmu_version'} = $binNMUver;
1014         $pkg->{'binary_nmu_changelog'} = $fail_reason;
1015         $pkg->{'notes'} = 'out-of-date';
1016         log_ta( $pkg, "--binNMU" );
1017         update_source_info($pkg);
1018         print "${name}: registered for binNMU $fullver\n" if $verbose;
1019 }
1020
1021 sub set_one_buildpri {
1022         my $name = shift;
1023         my $version = shift;
1024         my $key = shift;
1025         my $pkg = get_source_info($name);
1026         my $state;
1027
1028         if (!defined($pkg)) {
1029                 print "$name: not registered; can't set priority.\n";
1030                 return;
1031         }
1032         $state = $pkg->{'state'};
1033
1034         if ($state eq "Not-For-Us") {
1035                 print "$name: not suitable for this architecture. Skipping.\n";
1036                 return;
1037         } elsif ($state eq "Failed-Removed") {
1038                 print "$name: failed previously and doesn't need building. Skipping.\n";
1039                 return;
1040         }
1041         if (!pkg_version_eq($pkg, $version)) {
1042                 print "$name: version mismatch ($pkg->{'version'} registered). ",
1043                           "Skipping.\n";
1044                 return;
1045         }
1046         if ( $build_priority == 0 ) {
1047                 delete $pkg->{'buildpri'}
1048                         if $key eq 'permbuildpri' and defined $pkg->{'buildpri'}
1049                         and $pkg->{'buildpri'} == $pkg->{$key};
1050                 delete $pkg->{$key};
1051         } else {
1052                 $pkg->{'buildpri'} = $build_priority
1053                         if $key eq 'permbuildpri';
1054                 $pkg->{$key} = $build_priority;
1055         }
1056         update_source_info($pkg);
1057         print "$name: set to build priority $build_priority\n" if $verbose;
1058 }
1059
1060 sub add_one_depwait {
1061         my $name = shift;
1062         my $version = shift;
1063         my $state;
1064         my $pkg = get_source_info($name);
1065
1066         if (!defined($pkg)) {
1067                 print "$name: not registered yet.\n";
1068                 return;
1069         }
1070         $state = $pkg->{'state'};
1071
1072         if ($state eq "Dep-Wait") {
1073                 print "$name: merging with previously registered dependencies\n";
1074         }
1075         
1076         if (isin( $state, qw(Needs-Build Failed BD-Uninstallable))) {
1077                 print "$name: Warning: not registered for building previously, ".
1078                           "but processing anyway.\n";
1079         }
1080         elsif ($state eq "Not-For-Us") {
1081                 print "$name: not suitable for this architecture anyway. Skipping.\n";
1082                 return;
1083         }
1084         elsif ($state eq "Failed-Removed") {
1085                 print "$name: failed previously and doesn't need building. Skipping.\n";
1086                 return;
1087         }
1088         elsif ($state eq "Installed") {
1089                 print "$name: Is already installed in archive. Skipping.\n";
1090                 return;
1091         }
1092         elsif ($state eq "Uploaded") {
1093                 print "$name: Is already uploaded. Skipping.\n";
1094                 return;
1095         }
1096         elsif ($pkg->{'builder'} &&
1097                    $user ne $pkg->{'builder'}) {
1098                 print "$name: not taken by you, but by ".
1099                           "$pkg->{'builder'}. Skipping.\n";
1100                 return;
1101         }
1102         elsif ( !pkg_version_eq($pkg,$version)) {
1103                 print "$name: version mismatch ".
1104                           "($pkg->{'version'} ".
1105                           "by $pkg->{'builder'})\n";
1106                 return;
1107         }
1108         elsif ($fail_reason =~ /^\s*$/ ||
1109                    !parse_deplist( $fail_reason, 1 )) {
1110                 print "$name: Bad dependency list\n";
1111                 return;
1112         }
1113         change_state( \$pkg, 'Dep-Wait' );
1114         $pkg->{'builder'} = $user;
1115         if (defined $pkg->{'permbuildpri'}) {
1116                 $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
1117         } else {
1118                 delete $pkg->{'buildpri'};
1119         }
1120         my $deplist = parse_deplist( $pkg->{'depends'} );
1121         my $new_deplist = parse_deplist( $fail_reason );
1122         # add new dependencies, maybe overwriting old entries
1123         foreach (keys %$new_deplist) {
1124                 $deplist->{$_} = $new_deplist->{$_};
1125         }
1126         $pkg->{'depends'} = build_deplist($deplist);
1127         log_ta( $pkg, "--dep-wait" );
1128         update_source_info($pkg);
1129         print "$name: registered as waiting for dependencies\n" if $verbose;
1130 }
1131
1132
1133 sub parse_sources {
1134         my %pkgs;
1135         my %srcver;
1136         my $name;
1137         my $full = shift;
1138
1139         local($/) = ""; # read in paragraph mode
1140         while( <> ) {
1141                 my( $version, $arch, $section, $priority, $builddep, $buildconf, $binaries );
1142                 s/\s*$//m;
1143                 /^Package:\s*(\S+)$/mi and $name = $1;
1144                 /^Version:\s*(\S+)$/mi and $version = $1;
1145                 /^Architecture:\s*(\S+)$/mi and $arch = $1;
1146                 /^Section:\s*(\S+)$/mi and $section = $1;
1147                 /^Priority:\s*(\S+)$/mi and $priority = $1;
1148                 /^Build-Depends:\s*(.*)$/mi and $builddep = $1;
1149                 /^Build-Conflicts:\s*(.*)$/mi and $buildconf = $1;
1150                 /^Binary:\s*(.*)$/mi and $binaries = $1;
1151
1152                 next if (defined $srcver{$name} and version_less( $version, $srcver{$name} ));
1153                 $srcver{$name} = $version;
1154
1155                 $pkgs{$name}{'ver'} = $version;
1156                 $pkgs{$name}{'bin'} = $binaries;
1157                 $pkgs{$name}{'dep'} = $builddep;
1158                 $pkgs{$name}{'conf'} = $buildconf;
1159                 my $pkg = get_source_info($name);
1160
1161                 if (defined $pkg) {
1162                         my $change = 0;
1163
1164                         if ($arch eq "all" && !version_less( $version, $pkg->{'version'} )) {
1165                                 # package is now Arch: all, delete it from db
1166                                 change_state( \$pkg, 'deleted' );
1167                                 log_ta( $pkg, "--merge-sources" );
1168                                 print "$name ($pkg->{'version'}): deleted ".
1169                                           "from database, because now Arch: all\n"
1170                                                   if $verbose;
1171                                 del_source_info($name);
1172                                 next;
1173                         }
1174
1175                         # The "Version" should always be the source version --
1176                         # not a possible binNMU version number.
1177                         $pkg->{'version'} = $version, $change++
1178                                 if ($pkg->{'state'} eq 'Installed' and
1179                                 !version_eq( $pkg->{'version'}, $version));
1180                         # Always update priority and section, if available
1181                         $pkg->{'priority'} = $priority, $change++
1182                                 if defined $priority and (not defined($pkg->{'priority'}) or $pkg->{'priority'} ne $priority);
1183
1184                         $pkg->{'section'} = $section, $change++
1185                                 if defined $section and (not defined($pkg->{'section'}) or $pkg->{'section'} ne $section);
1186
1187                         update_source_info($pkg) if $change;
1188                 }
1189         }
1190         # Now that we only have the latest source version, build the list
1191         # of binary packages from the Sources point of view
1192         foreach $name (keys %pkgs) {
1193             foreach my $bin (split( /\s*,\s*/, $pkgs{$name}{'bin'} ) ) {
1194                 $merge_binsrc{$bin} = $name;
1195             }
1196         }
1197         # remove installed packages that no longer have source available
1198         # or binaries installed
1199         my $db = get_all_source_info();
1200         foreach $name (keys %$db) {
1201                 next if $name =~ /^_/;
1202                 my $pkg = $db->{$name};
1203                 if (not defined($pkgs{$name})) {
1204                         change_state( \$pkg, 'deleted' );
1205                         log_ta( $pkg, "--merge-sources" );
1206                         print "$name ($pkg->{'version'}): ".
1207                                   "deleted from database, because ".
1208                                   "not in Sources anymore\n"
1209                                           if $verbose;
1210                         del_source_info($name);
1211                 } else {
1212                         next if !isin( $pkg->{'state'}, qw(Installed) );
1213                         if ($full && not defined $merge_srcvers{$name}) {
1214                             change_state( \$pkg, 'deleted' );
1215                             log_ta( $pkg, "--merge-sources" );
1216                             print "$name ($pkg->{'version'}): ".
1217                                       "deleted from database, because ".
1218                                       "binaries don't exist anymore\n"
1219                                               if $verbose;
1220                             del_source_info($name);
1221                         } elsif ($full && version_less( $merge_srcvers{$name}, $pkg->{'version'})) {
1222                             print "$name ($pkg->{'version'}): ".
1223                                       "package is Installed but binaries are from ".
1224                                       $merge_srcvers{$name}. "\n"
1225                                               if $verbose;
1226                         }
1227                 }
1228         }
1229         return \%pkgs;
1230 }
1231
1232 # This function looks through a Packages file and sets the state of
1233 # packages to 'Installed'
1234 sub parse_packages {
1235         my $installed;
1236
1237         local($/) = ""; # read in paragraph mode
1238         while( <> ) {
1239                 my( $name, $version, $depends, $source, $sourcev, $architecture, $provides, $binaryv, $binnmu );
1240                 s/\s*$//m;
1241                 /^Package:\s*(\S+)$/mi and $name = $1;
1242                 /^Version:\s*(\S+)$/mi and $version = $1;
1243                 /^Depends:\s*(.*)$/mi and $depends = $1;
1244                 /^Source:\s*(\S+)(\s*\((\S+)\))?$/mi and ($source,$sourcev) = ($1, $3);
1245                 /^Architecture:\s*(\S+)$/mi and $architecture = $1;
1246                 /^Provides:\s*(.*)$/mi and $provides = $1;
1247                 next if !$name || !$version;
1248                 next if ($arch ne $architecture and $architecture ne "all");
1249                 next if (defined ($installed->{$name}) and $installed->{$name}{'version'} ne "" and
1250                         version_lesseq( $version, $installed->{$name}{'version'} ));
1251                 $installed->{$name}{'version'} = $version;
1252                 $installed->{$name}{'depends'} = $depends;
1253                 $installed->{$name}{'all'} = 1 if $architecture eq "all";
1254                 undef $installed->{$name}{'Provider'};
1255                 $installed->{$name}{'Source'} = $source ? $source : $name;
1256                 if ($provides) {
1257                     foreach (split( /\s*,\s*/, $provides )) {
1258                         if (not defined ($installed->{$_})) {
1259                             $installed->{$_}{'version'} = "";
1260                             $installed->{$_}{'Provider'} = $name;
1261                         }
1262                     }
1263                 }
1264                 if ( $version =~ /\+b(\d+)$/ ) {
1265                     $binnmu = $1;
1266                 }
1267                 $version = $sourcev if $sourcev;
1268                 $binaryv = $version;
1269                 $binaryv =~ s/\+b\d+$//;
1270                 $installed->{$name}{'Sourcev'} = $sourcev ? $sourcev : $binaryv;
1271                 $binaryv .= "+b$binnmu" if defined($binnmu);
1272
1273                 next if $architecture ne $arch;
1274                 $name = $source if $source;
1275                 next if defined($merge_srcvers{$name}) and $merge_srcvers{$name} eq $version;
1276                 $merge_srcvers{$name} = $version;
1277
1278                 my $pkg = get_source_info($name);
1279
1280                 if (defined $pkg) {
1281                         if (isin( $pkg->{'state'}, qw(Not-For-Us)) ||
1282                                 (isin($pkg->{'state'}, qw(Installed)) &&
1283                                  version_lesseq($binaryv, $pkg->{'installed_version'}))) {
1284                                 print "Skipping $name because State == $pkg->{'state'}\n"
1285                                         if $verbose >= 2;
1286                                 next;
1287                         }
1288                         if ($pkg->{'binary_nmu_version'} ) {
1289                                 my $nmuver = binNMU_version($pkg->{'version'}, $pkg->{'binary_nmu_version'});
1290                                 if (version_less( $binaryv, $nmuver )) {
1291                                         print "Skipping $name ($version) because have newer ".
1292                                                 "version ($nmuver) in db.\n"
1293                                                         if $verbose >= 2;
1294                                         next;
1295                                 }
1296                         } elsif (version_less($version, $pkg->{'version'})) {
1297                                 print "Skipping $name ($version) because have newer ".
1298                                         "version ($pkg->{'version'}) in db.\n"
1299                                                 if $verbose >= 2;
1300                                 next;
1301                         }
1302
1303                         if (!pkg_version_eq($pkg, $version) &&
1304                            $pkg->{'state'} ne "Installed") {
1305                                 warn "Warning: $name: newer version than expected appeared ".
1306                                          "in archive ($version vs. $pkg->{'version'})\n";
1307                                 delete $pkg->{'builder'};
1308                         }
1309
1310                         if (!isin( $pkg->{'state'}, qw(Uploaded) )) {
1311                                 warn "Warning: Package $name was not in uploaded state ".
1312                                          "before (but in '$pkg->{'state'}').\n";
1313                                 delete $pkg->{'builder'};
1314                                 delete $pkg->{'depends'};
1315                         }
1316                 } else {
1317                         $pkg = {};
1318                         $pkg->{'version'} = $version;
1319                 }
1320                 
1321                 change_state( \$pkg, 'Installed' );
1322                 $pkg->{'package'} = $name;
1323                 $pkg->{'installed_version'} = $binaryv;
1324                 if (defined $pkg->{'permbuildpri'}) {
1325                         $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
1326                 } else {
1327                         delete $pkg->{'buildpri'};
1328                 }
1329                 $pkg->{'version'} = $version
1330                         if version_less( $pkg->{'version'}, $version);
1331                 delete $pkg->{'binary_nmu_version'};
1332                 delete $pkg->{'binary_nmu_changelog'};
1333                 log_ta( $pkg, "--merge-packages" );
1334                 update_source_info($pkg);
1335                 print "$name ($version) is up-to-date now.\n" if $verbose;
1336         }
1337
1338         check_dep_wait( "--merge-packages", $installed );
1339         return $installed;
1340 }
1341
1342 sub pretend_avail {
1343         my ($package, $name, $version, $installed);
1344         
1345         foreach $package (@_) {
1346                 $package =~ s,^.*/,,; # strip path
1347                 $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
1348                 $package =~ s/_[\w\d]+\.changes$//; # strip extension
1349                 if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
1350                         ($name,$version) = ($1,$2);
1351                 }
1352                 else {
1353                         warn "$package: can't extract package name and version ".
1354                                  "(bad format)\n";
1355                         next;
1356                 }
1357                 $installed->{$name}{'version'} = $version;
1358         }
1359
1360         check_dep_wait( "--pretend-avail", $installed );
1361 }
1362
1363 sub check_dep_wait {
1364         my $action = shift;
1365         my $installed = shift;
1366         
1367         # check all packages in state Dep-Wait if dependencies are all
1368         # available now
1369         my $name;
1370         my $db = get_all_source_info();
1371         foreach $name (keys %$db) {
1372                 next if $name =~ /^_/;
1373                 my $pkg = $db->{$name};
1374                 next if $pkg->{'state'} ne "Dep-Wait";
1375                 my $deps = $pkg->{'depends'};
1376                 if (!$deps) {
1377                         print "$name: was in state Dep-Wait, but with empty ",
1378                                   "dependencies!\n";
1379                         goto make_needs_build;
1380                 }
1381                 my $deplist = parse_deplist($deps);
1382                 my $new_deplist;
1383                 my $allok = 1;
1384                 my @removed_deps;
1385                 foreach (keys %$deplist) {
1386                         if (!exists $installed->{$_} ||
1387                                 ($deplist->{$_}->{'rel'} && $deplist->{$_}->{'version'} &&
1388                                  !version_compare( $installed->{$_}{'version'},
1389                                                                    $deplist->{$_}->{'rel'},
1390                                                                    $deplist->{$_}->{'version'}))) {
1391                                 $allok = 0;
1392                                 $new_deplist->{$_} = $deplist->{$_};
1393                         }
1394                         else {
1395                                 push( @removed_deps, $_ );
1396                         }
1397                 }
1398                 if ($allok) {
1399                   make_needs_build:
1400                         change_state( \$pkg, 'Needs-Build' );
1401                         log_ta( $pkg, $action );
1402                         delete $pkg->{'builder'};
1403                         delete $pkg->{'depends'};
1404                         print "$name ($pkg->{'version'}) has all ",
1405                                   "dependencies available now\n" if $verbose;
1406                         $new_vers{$name}++;
1407                         update_source_info($pkg);
1408                 }
1409                 elsif (@removed_deps) {
1410                         $pkg->{'depends'} = build_deplist( $new_deplist );
1411                         print "$name ($pkg->{'version'}): some dependencies ",
1412                                   "(@removed_deps) available now, but not all yet\n"
1413                                 if $verbose;
1414                         update_source_info($pkg);
1415                 }
1416         }
1417 }
1418
1419 # This function accepts quinn-diff output (either from a file named on
1420 # the command line, or on stdin) and sets the packages named there to
1421 # state 'Needs-Build'.
1422 sub parse_quinn_diff {
1423         my $partial = shift;
1424         my %quinn_pkgs;
1425         my $dubious = "";
1426         
1427         while( <> ) {
1428                 my $change = 0;
1429                 next if !m,^([-\w\d/]*)/                        # section
1430                                ([-\w\d.+]+)_                    # package name
1431                                    ([\w\d:.~+-]+)\.dsc\s*       # version
1432                                    \[([^:]*):                           # priority
1433                                    ([^]]+)\]\s*$,x;                     # rest of notes
1434                 my($section,$name,$version,$priority,$notes) = ($1, $2, $3, $4, $5);
1435                 $quinn_pkgs{$name}++;
1436                 $section ||= "unknown";
1437                 $priority ||= "unknown";
1438                 $priority = "unknown" if $priority eq "-";
1439                 $priority = "standard" if ($name eq "debian-installer");
1440
1441                 my $pkg = get_source_info($name);
1442
1443                 # Always update section and priority.
1444                 if (defined($pkg)) {
1445
1446                         $pkg->{'section'}  = $section, $change++ if not defined
1447                                 $pkg->{'section'} or $section ne "unknown";
1448                         $pkg->{'priority'} = $priority, $change++ if not defined
1449                                 $pkg->{'priority'} or $priority ne "unknown";
1450                 }
1451
1452                 if (defined($pkg) &&
1453                         $pkg->{'state'} =~ /^Dep-Wait/ &&
1454                         version_less( $pkg->{'version'}, $version )) {
1455                         change_state( \$pkg, 'Dep-Wait' );
1456                         $pkg->{'version'}  = $version;
1457                         delete $pkg->{'binary_nmu_version'};
1458                         delete $pkg->{'binary_nmu_changelog'};
1459                         log_ta( $pkg, "--merge-quinn" );
1460                         $change++;
1461                         print "$name ($version) still waiting for dependencies.\n"
1462                                 if $verbose;
1463                 }
1464                 elsif (defined($pkg) &&
1465                            $pkg->{'state'} =~ /-Removed$/ &&
1466                            version_eq($pkg->{'version'}, $version)) {
1467                         # reinstantiate a package that has been removed earlier
1468                         # (probably due to a quinn-diff malfunction...)
1469                         my $newstate = $pkg->{'state'};
1470                         $newstate =~ s/-Removed$//;
1471                         change_state( \$pkg, $newstate );
1472                         $pkg->{'version'}  = $version;
1473                         $pkg->{'notes'}    = $notes;
1474                         log_ta( $pkg, "--merge-quinn" );
1475                         $change++;
1476                         print "$name ($version) reinstantiated to $newstate.\n"
1477                                 if $verbose;
1478                 }
1479                 elsif (defined($pkg) &&
1480                            $pkg->{'state'} eq "Not-For-Us" &&
1481                            version_less( $pkg->{'version'}, $version )) {
1482                         # for Not-For-Us packages just update the version etc., but
1483                         # keep the state
1484                         change_state( \$pkg, "Not-For-Us" );
1485                         $pkg->{'package'}  = $name;
1486                         $pkg->{'version'}  = $version;
1487                         $pkg->{'notes'}    = $notes;
1488                         delete $pkg->{'builder'};
1489                         log_ta( $pkg, "--merge-quinn" );
1490                         $change++;
1491                         print "$name ($version) still Not-For-Us.\n" if $verbose;
1492                 }
1493                 elsif (!defined($pkg) ||
1494                            $pkg->{'state'} ne "Not-For-Us" &&
1495                            (version_less( $pkg->{'version'}, $version ) ||
1496                            ($pkg->{'state'} eq "Installed" && version_less($pkg->{'installed_version'}, $version)))) {
1497
1498                         if (defined( $pkg->{'state'} ) && isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
1499                                 send_mail( $pkg->{'builder'},
1500                                                    "new version of $name (dist=$distribution)",
1501                                                    "As far as I'm informed, you're currently ".
1502                                                    "building the package $name\n".
1503                                                    "in version $pkg->{'version'}.\n\n".
1504                                                    "Now there's a new source version $version. ".
1505                                                    "If you haven't finished\n".
1506                                                    "compiling $name yet, you can stop it to ".
1507                                                    "save some work.\n".
1508                                                    "Just to inform you...\n".
1509                                                    "(This is an automated message)\n" );
1510                                 print "$name: new version ($version) while building ".
1511                                           "$pkg->{'version'} -- sending mail ".
1512                                           "to builder ($pkg->{'builder'})\n"
1513                                   if $verbose;
1514                         }
1515                         change_state( \$pkg, 'Needs-Build' );
1516                         $pkg->{'package'}  = $name;
1517                         $pkg->{'version'}  = $version;
1518                         $pkg->{'section'}  = $section;
1519                         $pkg->{'priority'} = $priority;
1520                         $pkg->{'notes'}    = $notes;
1521                         delete $pkg->{'builder'};
1522                         delete $pkg->{'binary_nmu_version'};
1523                         delete $pkg->{'binary_nmu_changelog'};
1524                         log_ta( $pkg, "--merge-quinn" );
1525                         $new_vers{$name}++;
1526                         $change++;
1527                         print "$name ($version) needs rebuilding now.\n" if $verbose;
1528                 }
1529                 elsif (defined($pkg) &&
1530                            !version_eq( $pkg->{'version'}, $version ) &&
1531                            isin( $pkg->{'state'}, qw(Installed Not-For-Us) )) {
1532                         print "$name: skipping because version in db ".
1533                                   "($pkg->{'version'}) is >> than ".
1534                                   "what quinn-diff says ($version) ".
1535                                   "(state is $pkg->{'state'})\n"
1536                                           if $verbose;
1537                         $dubious .= "$pkg->{'state'}: ".
1538                                                 "db ${name}_$pkg->{'version'} >> ".
1539                                                 "quinn $version\n" if !$partial;
1540                 }
1541                 elsif ($verbose >= 2) {
1542                         if ($pkg->{'state'} eq "Not-For-Us") {
1543                                 print "Skipping $name because State == ".
1544                                           "$pkg->{'state'}\n";
1545                         }
1546                         elsif (!version_less($pkg->{'version'}, $version)) {
1547                                 print "Skipping $name because version in db ".
1548                                           "($pkg->{'version'}) is >= than ".
1549                                           "what quinn-diff says ($version)\n";
1550                         }
1551                 }
1552                 update_source_info($pkg) if $change;
1553         }
1554
1555         if ($dubious) {
1556                 send_mail( $conf::db_maint,
1557                                    "Dubious versions in " . table_name() . " "
1558                                    . $distribution . " table",
1559                                    "The following packages have a newer version in the ".
1560                                    "wanna-build database\n".
1561                                    "than what quinn-diff says, and this is strange for ".
1562                                    "their state\n".
1563                                    "It could be caused by a lame mirror, or the version ".
1564                                    "in the database\n".
1565                                    "is wrong.\n\n".
1566                                    $dubious );
1567         }
1568
1569         # Now re-check the DB for packages in states Needs-Build, Failed,
1570         # Dep-Wait or BD-Uninstallable and remove them if they're not listed
1571         # anymore by quinn-diff.
1572         if ( !$partial ) {
1573                 my $name;
1574                 my $db = get_all_source_info();
1575                 foreach $name (keys %$db) {
1576                         next if $name =~ /^_/;
1577                         my $pkg = $db->{$name};
1578                         next if defined $pkg->{'binary_nmu_version'};
1579                         next if !isin( $pkg->{'state'},
1580                                                    qw(Needs-Build Building Built Build-Attempted Uploaded Failed Dep-Wait BD-Uninstallable) );
1581                         my $virtual_delete = $pkg->{'state'} eq 'Failed';
1582                                                                  
1583                         if (!$quinn_pkgs{$name}) {
1584                                 change_state( \$pkg, $virtual_delete ?
1585                                                           $pkg->{'state'}."-Removed" :
1586                                                           'deleted' );
1587                                 log_ta( $pkg, "--merge-quinn" );
1588                                 print "$name ($pkg->{'version'}): ".
1589                                           ($virtual_delete ? "(virtually) " : "") . "deleted ".
1590                                           "from database, because not in quinn-diff anymore\n"
1591                                                   if $verbose;
1592                                 if ($virtual_delete) {
1593                                     update_source_info($pkg);
1594                                 } else {
1595                                     del_source_info($name);
1596                                 }
1597                         }
1598                 }
1599         }
1600 }
1601
1602
1603 # for sorting priorities and sections
1604 BEGIN {
1605         %prioval = ( required             => -5,
1606                                  important            => -4,
1607                                  standard             => -3,
1608                                  optional             => -2,
1609                                  extra                => -1,
1610                                  unknown              => -1 );
1611         %sectval = ( 
1612                                  libs                   => -200,
1613                                  'debian-installer'     => -199,
1614                                  base                   => -198,
1615                                  devel                  => -197,
1616                                  kernel                 => -196,
1617                                  shells                 => -195,
1618                                  perl                   => -194,
1619                                  python                 => -193,
1620                                  graphics               => -192,
1621                                  admin                  => -191,
1622                                  utils                  => -190,
1623                                  x11                    => -189,
1624                                  editors                => -188,
1625                                  net                    => -187,
1626                                  httpd                  => -186,
1627                                  mail                   => -185,
1628                                  news                   => -184,
1629                                  tex                    => -183,
1630                                  text                   => -182,
1631                                  web                    => -181,
1632                                  vcs                    => -180,
1633                                  doc                    => -179,
1634                                  localizations          => -178,
1635                                  interpreters           => -177,
1636                                  ruby                   => -176,
1637                                  java                   => -175,
1638                                  ocaml                  => -174,
1639                                  lisp                   => -173,
1640                                  haskell                => -172,
1641                                  'cli-mono'             => -171,
1642                                  gnome                  => -170,
1643                                  kde                    => -169,
1644                                  xfce                   => -168,
1645                                  gnustep                => -167,
1646                                  database               => -166,
1647                                  video                  => -165,
1648                                  debug                  => -164,
1649                                  games                  => -163,
1650                                  misc                   => -162,
1651                                  fonts                  => -161,
1652                                  otherosfs              => -160,
1653                                  oldlibs                => -159,
1654                                  libdevel               => -158,
1655                                  sound                  => -157,
1656                                  math                   => -156,
1657                                  'gnu-r'                => -155,
1658                                  science                => -154,
1659                                  comm                   => -153,
1660                                  electronics            => -152,
1661                                  hamradio               => -151,
1662                                  embedded               => -150,
1663                                  php                    => -149,
1664                                  zope                   => -148,
1665         );
1666         foreach my $i (keys %sectval) {
1667                 $sectval{"contrib/$i"} = $sectval{$i}+40;
1668                 $sectval{"non-free/$i"} = $sectval{$i}+80;
1669         }
1670         $sectval{'unknown'}     = -165;
1671
1672         %catval =  ( "none"                           => -20,
1673                                  "uploaded-fixed-pkg" => -19,
1674                                  "fix-expected"       => -18,
1675                                  "reminder-sent"      => -17,
1676                                  "nmu-offered"        => -16,
1677                                  "easy"               => -15,
1678                                  "medium"                     => -14,
1679                                  "hard"                   => -13,
1680                                  "compiler-error"     => -12 );
1681 }
1682
1683 sub sort_list_func {
1684         my( $letter, $x, $ax, $bx );
1685
1686         foreach $letter (split( "", $list_order )) {
1687           SWITCH: foreach ($letter) {
1688                   /P/ && do {
1689                         $x = $b->{'buildpri'} <=> $a->{'buildpri'};
1690                         return $x if $x != 0;
1691                         last SWITCH;
1692                   };
1693                   /p/ && do {
1694                           $x = $prioval{$a->{'priority'}} <=> $prioval{$b->{'priority'}};
1695                           return $x if $x != 0;
1696                           last SWITCH;
1697                   };
1698                   /s/ && do {
1699                           $x = $sectval{$a->{'section'}} <=> $sectval{$b->{'section'}};
1700                           return $x if $x != 0;
1701                           last SWITCH;
1702                   };
1703                   /n/ && do {
1704                           $x = $a->{'package'} cmp $b->{'package'};
1705                           return $x if $x != 0;
1706                           last SWITCH;
1707                   };
1708                   /b/ && do {
1709                           $x = $a->{'builder'} cmp $b->{'builder'};
1710                           return $x if $x != 0;
1711                           last SWITCH;
1712                   };
1713                   /c/ && do {
1714                           $ax = ($a->{'notes'} =~ /^(out-of-date|partial)/) ? 0 :
1715                                     ($a->{'notes'} =~ /^uncompiled/) ? 2 : 1;
1716                           $bx = ($b->{'notes'} =~ /^(out-of-date|partial)/) ? 0 :
1717                                     ($b->{'notes'} =~ /^uncompiled/) ? 2 : 1;
1718                           $x = $ax <=> $bx;
1719                           return $x if $x != 0;
1720                           last SWITCH;
1721                   };
1722                   /f/ && do {
1723                           my $ca = defined $a->{'failed_category'} ?
1724                                   $a->{'failed_category'} : "none";
1725                           my $cb = defined $b->{'failed_category'} ?
1726                                   $b->{'failed_category'} : "none";
1727                           $x = $catval{$ca} <=> $catval{$cb};
1728                           return $x if $x != 0;
1729                           last SWITCH;
1730                   };
1731                   /S/ && do {
1732                           my $pa = $prioval{$a->{'priority'}} >
1733                                   $prioval{'standard'};
1734                           my $pb = $prioval{$b->{'priority'}} >
1735                                   $prioval{'standard'};
1736                           $x = $pa <=> $pb;
1737                           return $x if $x != 0;
1738                           last SWITCH;
1739                   };
1740           }
1741         }
1742         return 0;
1743 }
1744
1745 sub list_packages {
1746         my $state = shift;
1747         my( $name, $pkg, @list );
1748         my $cnt = 0;
1749         my %scnt;
1750         my $ctime = time;
1751
1752         my $db = get_all_source_info();
1753         foreach $name (keys %$db) {
1754                 next if $name =~ /^_/;
1755                 $pkg = $db->{$name};
1756                 next if $state ne "all" && $pkg->{'state'} !~ /^\Q$state\E$/i;
1757                 next if $user && (lc($state) ne 'needs-build' and $pkg->{'builder'} ne $user);
1758                 next if $category && $pkg->{'state'} eq "Failed" &&
1759                                 $pkg->{'failed_category'} ne $category;
1760                 next if ($list_min_age > 0 &&
1761                                  ($ctime-parse_date($pkg->{'State-Change'})) < $list_min_age)||
1762                                 ($list_min_age < 0 &&
1763                                  ($ctime-parse_date($pkg->{'State-Change'})) > -$list_min_age);
1764                 push( @list, $pkg );
1765         }
1766
1767         foreach $pkg (sort sort_list_func @list) {
1768                 print "$pkg->{'section'}/$pkg->{'package'}_$pkg->{'version'}";
1769                 print ": $pkg->{'state'}"
1770                         if $state eq "all";
1771                 print " by $pkg->{'builder'}"
1772                         if $pkg->{'state'} ne "Needs-Build" && $pkg->{'builder'};
1773                 print " [$pkg->{'priority'}:$pkg->{'notes'}";
1774                 print ":PREV-FAILED"
1775                         if $pkg->{'previous_state'} =~ /^Failed/;
1776                 print ":bp{" . $pkg->{'buildpri'} . "}"
1777                         if defined $pkg->{'buildpri'};
1778                 print ":binNMU{" . $pkg->{'binary_nmu_version'} . "}"
1779                         if defined $pkg->{'binary_nmu_version'};
1780                 print "]\n";
1781                 print "  Reasons for failing:\n",
1782                           "    [Category: ",
1783                           defined $pkg->{'failed_category'} ? $pkg->{'failed_category'} : "none",
1784                           "]\n    ",
1785                           join("\n    ",split("\n",$pkg->{'failed'})), "\n"
1786                         if $pkg->{'state'} =~ /^Failed/;
1787                 print "  Dependencies: $pkg->{'depends'}\n"
1788                         if $pkg->{'state'} eq "Dep-Wait";
1789                 print "  Reasons for BD-Uninstallable:\n    ",
1790                           join("\n    ",split("\n",$pkg->{'bd_problem'})), "\n"
1791                         if $pkg->{'state'} eq "BD-Uninstallable";
1792                 print "  Previous state was $pkg->{'previous_state'} until ",
1793                           "$pkg->{'State-Change'}\n"
1794                         if $verbose && $pkg->{'previous_state'};
1795                 print "  No previous state recorded\n"
1796                         if $verbose && !$pkg->{'previous_state'};
1797                 print "  Previous failing reasons:\n    ",
1798                       join("\n    ",split("\n",$pkg->{'old_failed'})), "\n"
1799                         if $verbose && $pkg->{'old_failed'};
1800                 ++$cnt;
1801                 $scnt{$pkg->{'state'}}++ if $state eq "all";
1802         }
1803         if ($state eq "all") {
1804                 foreach (sort keys %scnt) {
1805                         print "Total $scnt{$_} package(s) in state $_.\n";
1806                 }
1807         }
1808         print "Total $cnt package(s)\n";
1809         
1810 }
1811
1812 sub info_packages {
1813         my( $name, $pkg, $key, $dist );
1814         my @firstkeys = qw(package version builder state section priority
1815                                            installed_version previous_state state_change);
1816         my @dists = $info_all_dists ? keys %conf::distributions : ($distribution);
1817         
1818         foreach $name (@_) {
1819                 $name =~ s/_.*$//; # strip version
1820                 foreach $dist (@dists) {
1821                         my $pname = "$name" . ($info_all_dists ? "($dist)" : "");
1822                         
1823                         $pkg = get_source_info($name);
1824                         if (!defined( $pkg )) {
1825                                 print "$pname: not registered\n";
1826                                 next;
1827                         }
1828
1829                         print "$pname:\n";
1830                         foreach $key (@firstkeys) {
1831                                 next if !defined $pkg->{$key};
1832                                 my $val = $pkg->{$key};
1833                                 chomp( $val );
1834                                 $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
1835                                 $val =~ s/\n/\n    /g;
1836                                 printf "  %-20s: %s\n", $key, $val;
1837                         }
1838                         foreach $key (sort keys %$pkg) {
1839                                 next if isin( $key, @firstkeys );
1840                                 my $val = $pkg->{$key};
1841                                 chomp( $val );
1842                                 $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
1843                                 $val =~ s/\n/\n    /g;
1844                                 printf "  %-20s: %s\n", $key, $val;
1845                         }
1846                 }
1847         }
1848 }
1849
1850 sub forget_packages {
1851         my( $name, $pkg, $key, $data );
1852         
1853         foreach $name (@_) {
1854                 $name =~ s/_.*$//; # strip version
1855                 $pkg = get_source_info($name);
1856                 if (!defined( $pkg )) {
1857                         print "$name: not registered\n";
1858                         next;
1859                 }
1860
1861                 $data = "";
1862                 foreach $key (sort keys %$pkg) {
1863                         my $val = $pkg->{$key};
1864                         chomp( $val );
1865                         $val =~ s/\n/\n /g;
1866                         $data .= sprintf "  %-20s: %s\n", $key, $val;
1867                 }
1868                 send_mail( $conf::db_maint,
1869                                    "$name deleted from DB " . table_name() . " " . $distribution,
1870                                    "The package '$name' has been deleted from the database ".
1871                                    "by $user.\n\n".
1872                                    "Data registered about the deleted package:\n".
1873                                    "$data\n" ) if $conf::db_maint;
1874                 change_state( \$pkg, 'deleted' );
1875                 log_ta( $pkg, "--forget" );
1876                 del_source_info($name);
1877                 print "$name: deleted from database\n" if $verbose;
1878         }
1879 }
1880
1881 sub forget_users {
1882         $dbh->do("DELETE from " . user_table_name() . 
1883                 " WHERE distribution = ?", undef, $distribution) or die $dbh->errstr;
1884 }
1885
1886 sub read_db {
1887         my $file = shift;
1888
1889         print "Reading ASCII database from $file..." if $verbose >= 1;
1890         open( F, "<$file" ) or
1891                 die "Can't open database $file: $!\n";
1892
1893         local($/) = ""; # read in paragraph mode
1894         while( <F> ) {
1895                 my( %thispkg, $name );
1896                 s/[\s\n]+$//;
1897                 s/\n[ \t]+/\376\377/g;  # fix continuation lines
1898                 s/\376\377\s*\376\377/\376\377/og;
1899   
1900                 while( /^(\S+):[ \t]*(.*)[ \t]*$/mg ) {
1901                         my ($key, $val) = ($1, $2);
1902                         $key =~ s/-/_/g;
1903                         $key =~ tr/A-Z/a-z/;
1904                         $val =~ s/\376\377/\n/g;
1905                         $thispkg{$key} = $val;
1906                 }
1907                 check_entry( \%thispkg );
1908                 # add to db
1909                 if (exists($thispkg{'package'})) {
1910                         update_source_info(\%thispkg);
1911                 }
1912                 elsif(exists($thispkg{'user'})) {
1913                         # user in import, username in database.
1914                         $dbh->do('INSERT INTO ' . user_table_name() .
1915                                         ' (username, distribution, last_seen)' .
1916                                         ' values (?, ?, ?)',
1917                                 undef, $thispkg{'user'}, $distribution,
1918                                 $thispkg{'last_seen'})
1919                                 or die $dbh->errstr;
1920                  }
1921         }
1922         close( F );
1923         print "done\n" if $verbose >= 1;
1924 }
1925
1926 sub check_entry {
1927         my $pkg = shift;
1928         my $field;
1929
1930         return if $op_mode eq "manual-edit"; # no checks then
1931         
1932         # check for required fields
1933         if (exists $pkg->{'user'}) {
1934                 return;
1935         }
1936         if (!exists $pkg->{'package'}) {
1937                 print STDERR "Bad entry: ",
1938                           join( "\n", map { "$_: $pkg->{$_}" } keys %$pkg ), "\n";
1939                 die "Database entry lacks package or username field\n";
1940         }
1941         if (!exists $pkg->{'version'}) {
1942                 die "Database entry for $pkg->{'package'} lacks Version: field\n";
1943         }
1944         # if no State: field, generate one (for old db compat)
1945         if (!exists($pkg->{'state'})) {
1946                 $pkg->{'state'} =
1947                         exists $pkg->{'failed'} ? 'Failed' : 'Building';
1948         }
1949         # check state field
1950         die "Bad state $pkg->{'state'} of package $pkg->{Package}\n"
1951                 if !isin( $pkg->{'state'},
1952                                   qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait
1953                                          Failed Failed-Removed Not-For-Us BD-Uninstallable
1954                                          ) );
1955 }
1956
1957 sub change_state {
1958         my $pkgr = shift;
1959         my $pkg = $$pkgr;
1960         my $newstate = shift;
1961         my $state = \$pkg->{'state'};
1962         
1963         return if defined($$state) and $$state eq $newstate;
1964         $pkg->{'previous_state'} = $$state if defined($$state);
1965         $pkg->{'State-Change'} = $curr_date;
1966
1967         if (defined($$state) and $$state eq 'Failed') {
1968                 $pkg->{'old_failed'} =
1969                         "-"x20 . " $pkg->{'version'} " . "-"x20 . "\n" .
1970                         $pkg->{'failed'} . "\n" .
1971                         $pkg->{'old_failed'};
1972                 delete $pkg->{'failed'};
1973                 delete $pkg->{'failed_category'};
1974         }
1975         if (defined($$state) and $$state eq 'BD-Uninstallable') {
1976                 delete $pkg->{'bd_problem'};
1977         }
1978         $$state = $newstate;
1979 }
1980
1981 sub log_ta {
1982         my $pkg = shift;
1983         my $action = shift;
1984         my $dist = $distribution;
1985         my $str;
1986         my $prevstate;
1987
1988         $prevstate = $pkg->{'previous_state'};
1989         $str = "$action($dist): $pkg->{'package'}_$pkg->{'version'} ".
1990                    "changed from $prevstate to $pkg->{'state'} ".
1991                    "by $real_user as $user";
1992         
1993         my $transactlog = db_transactlog();
1994         if (!open( LOG, ">>$transactlog" )) {
1995                 warn "Can't open log file $transactlog: $!\n";
1996                 return;
1997         }
1998         print LOG "$curr_date: $str\n";
1999         close( LOG );
2000
2001         if (!($prevstate eq 'Failed' && $pkg->{'state'} eq 'Failed')) {
2002                 $str .= " (with --override)"
2003                         if $opt_override;
2004                 $mail_logs .= "$str\n";
2005         }
2006 }
2007
2008
2009 sub send_mail {
2010         my $to = shift;
2011         my $subject = shift;
2012         my $text = shift;
2013
2014         my $from = $conf::db_maint;
2015         my $domain = $conf::buildd_domain;
2016
2017         $from .= "\@$domain" if $from !~ /\@/;
2018
2019         $to .= '@' . $domain if $to !~ /\@/;
2020         $text =~ s/^\.$/../mg;
2021         local $SIG{'PIPE'} = 'IGNORE';
2022         open( PIPE,  "| $conf::mailprog -oem $to" )
2023                 or die "Can't open pipe to $conf::mailprog: $!\n";
2024         chomp $text;
2025         print PIPE "From: $from\n";
2026         print PIPE "Subject: $subject\n\n";
2027         print PIPE "$text\n";
2028         close( PIPE );
2029 }
2030
2031 sub db_transactlog {
2032         return "$conf::basedir/$arch/$conf::transactlog";
2033 }
2034
2035 # for parsing input to dep-wait
2036 sub parse_deplist {
2037     my $deps = shift;
2038     my $verify = shift;
2039     my %result;
2040     
2041     foreach (split( /\s*,\s*/, $deps )) {
2042         if ($verify) {
2043             # verification requires > starting prompts, no | crap
2044             if (!/^(\S+)\s*(\(\s*(>(?:[>=])?)\s*(\S+)\s*\))?\s*$/) {
2045                 return 0;
2046             }
2047             next;
2048         }
2049         my @alts = split( /\s*\|\s*/, $_ );
2050         # Anything with an | is ignored, as it can be configured on a
2051         # per-buildd basis what will be installed
2052         next if $#alts != 0;
2053         $_ = shift @alts;
2054
2055         if (!/^(\S+)\s*(\(\s*(>=|=|==|>|>>|<<|<=)\s*(\S+)\s*\))?\s*$/) {
2056             warn( "parse_deplist: bad dependency $_\n" );
2057             next;
2058         }
2059         my($dep, $rel, $relv) = ($1, $3, $4);
2060         $rel = ">>" if defined($rel) and $rel eq ">";
2061         $result{$dep}->{'package'} = $dep;
2062         if ($rel && $relv) {
2063             $result{$dep}->{'rel'} = $rel;
2064             $result{$dep}->{'version'} = $relv;
2065         }
2066     }
2067     return 1 if $verify;
2068     return \%result;
2069 }
2070
2071 # for parsing Build-Depends from Sources
2072 sub parse_srcdeplist {
2073     my $pkg = shift;
2074     my $deps = shift;
2075     my $arch = shift;
2076     my $dep;
2077     my @results;
2078     
2079     foreach $dep (split( /\s*,\s*/, $deps )) {
2080         my @alts = split( /\s*\|\s*/, $dep );
2081         # Anything with an | is ignored, as it can be configured on a
2082         # per-buildd basis what will be installed
2083         next if $#alts != 0;
2084         $_ = shift @alts;
2085         if (!/^([^\s([]+)\s*(\(\s*([<=>]+)\s*(\S+)\s*\))?(\s*\[([^]]+)\])?/) {
2086             warn( "parse_srcdeplist: bad dependency $_\n" );
2087             next;
2088         }
2089         my($dep, $rel, $relv, $archlist) = ($1, $3, $4, $6);
2090         if ($archlist) {
2091             $archlist =~ s/^\s*(.*)\s*$/$1/;
2092             my @archs = split( /\s+/, $archlist );
2093             my ($use_it, $ignore_it, $include) = (0, 0, 0);
2094             foreach (@archs) {
2095                 if (/^!/) {
2096                     $ignore_it = 1 if substr($_, 1) eq $arch;
2097                 } else {
2098                     $use_it = 1 if $_ eq $arch;
2099                     $include = 1;
2100                 }
2101             }
2102             warn "Warning: inconsistent arch restriction on ",
2103                  "$pkg: $dep depedency\n"
2104                  if $ignore_it && $use_it;
2105             next if $ignore_it || ($include && !$use_it);
2106         }
2107         my $neg = 0;
2108         if ($dep =~ /^!/) {
2109             $dep =~ s/^!\s*//;
2110             $neg = 1;
2111         }
2112         my $result;
2113         $result->{'package'} = $dep;
2114         $result->{'Neg'} = $neg;
2115         if ($rel && $relv) {
2116             $result->{'rel'} = $rel;
2117             $result->{'version'} = $relv;
2118         }
2119         push @results, $result;
2120     }
2121     return \@results;
2122 }
2123
2124 sub build_deplist {
2125         my $list = shift;
2126         my($key, $result);
2127         
2128         foreach $key (keys %$list) {
2129                 $result .= ", " if $result;
2130                 $result .= $key;
2131                 $result .= " ($list->{$key}->{'rel'} $list->{$key}->{'version'})"
2132                         if $list->{$key}->{'rel'} && $list->{$key}->{'version'};
2133         }
2134         return $result;
2135 }
2136
2137 sub get_unsatisfied_dep {
2138     my $bd  = shift;
2139     my $pkgs = shift;
2140     my $dep = shift;
2141     my $savedep = shift;
2142
2143     my $pkgname = $dep->{'package'};
2144
2145     if (defined $pkgs->{$pkgname}{'Provider'}) {
2146         # provides.  leave them for buildd/sbuild.
2147         return "";
2148     }
2149
2150     # check cache
2151     return $pkgs->{$pkgname}{'Unsatisfied'} if $savedep and defined($pkgs->{$pkgname}{'Unsatisfied'});
2152
2153     # Return unsatisfied deps to a higher caller to process
2154     if ((!defined($pkgs->{$pkgname})) or
2155         (defined($dep->{'rel'}) and !version_compare( $pkgs->{$pkgname}{'version'}, $dep->{'rel'}, $dep->{'Version'} ) ) ) {
2156         my %deplist;
2157         $deplist{$pkgname} = $dep;
2158         my $deps = build_deplist(\%deplist);
2159         $pkgs->{$pkgname}{'Unsatisfied'} = $deps if $savedep;
2160         return $deps;
2161     }
2162
2163     # set cache to "" to avoid infinite recursion
2164     $pkgs->{$pkgname}{'Unsatisfied'} = "" if $savedep;
2165
2166     if (defined $pkgs->{$dep->{'package'}}{'depends'}) {
2167         my $deps = parse_deplist( $pkgs->{$dep->{'package'}}{'depends'} );
2168         foreach (keys %$deps) {
2169             $dep = $$deps{$_};
2170             # recur on dep.
2171             my $ret = get_unsatisfied_dep($bd,$pkgs,$dep,1);
2172             if ($ret ne "") {
2173                 my $retdep = parse_deplist( $ret );
2174                 foreach (keys %$retdep) {
2175                     $dep = $$retdep{$_};
2176
2177                     $dep->{'rel'} = '>=' if defined($dep->{'rel'}) and $dep->{'rel'} =~ '^=';
2178
2179                     if (defined($dep->{'rel'}) and $dep->{'rel'} =~ '^>' and defined ($pkgs->{$dep->{'package'}}) and
2180                         version_compare($bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'ver'},'>>',$pkgs->{$dep->{'package'}}{'Sourcev'})) {
2181                         if (not defined($merge_binsrc{$dep->{'package'}})) {
2182                             # the uninstallable package doesn't exist in the new source; look for something else that does.
2183                             delete $$retdep{$dep->{'package'}};
2184                             foreach (sort (split( /\s*,\s*/, $bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'bin'}))) {
2185                                 next if ($pkgs->{$_}{'all'} or not defined $pkgs->{$_}{'version'});
2186                                 $dep->{'package'} = $_;
2187                                 $dep->{'rel'} = '>>';
2188                                 $dep->{'version'} = $pkgs->{$_}{'Version'};
2189                                 $$retdep{$_} = $dep;
2190                                 last;
2191                             }
2192                         }
2193                     } else {
2194                         # sanity check to make sure the depending binary still exists, and the depended binary exists and dep-wait on a new version of it
2195                         if ( defined($merge_binsrc{$pkgname}) and defined($pkgs->{$dep->{'package'}}{'version'}) ) {
2196                             delete $$retdep{$dep->{'package'}};
2197                             $dep->{'package'} = $pkgname;
2198                             $dep->{'rel'} = '>>';
2199                             $dep->{'version'} = $pkgs->{$pkgname}{'Version'};
2200                             $$retdep{$pkgname} = $dep;
2201                         }
2202                         delete $$retdep{$dep->{'package'}} if (defined ($dep->{'rel'}) and $dep->{'rel'} =~ '^>');
2203                     }
2204                 }
2205                 $ret = build_deplist($retdep);
2206                 $pkgs->{$pkgname}{'Unsatisfied'} = $ret if $savedep;
2207                 return $ret;
2208             }
2209         }
2210     }
2211     return "";
2212 }
2213
2214 sub call_edos_depcheck {
2215     my $packagesfile = shift;
2216     my $srcs = shift;
2217     my $key;
2218     
2219     return if defined ($conf::distributions{$distribution}{noadw});
2220
2221     # We need to check all of needs-build, as any new upload could make
2222     # something in needs-build have uninstallable deps
2223     # We also check everything in bd-uninstallable, as any new upload could
2224     # make that work again
2225     my %interesting_packages;
2226     my $db = get_all_source_info();
2227     foreach $key (keys %$db) {
2228         my $pkg = $db->{$key};
2229         if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/)) {
2230                 $interesting_packages{$key} = undef;
2231         }
2232     }
2233     
2234     #print "I would look at these sources with edos-depcheck:\n";
2235     #print join " ", keys %interesting_packages,"\n";
2236
2237     my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-";
2238     my ($tmpfile, $i);
2239     for( $i = 0;; ++$i ) {
2240             $tmpfile = $tmpfile_pattern . $i;
2241             last if ! -e $tmpfile;
2242     }
2243
2244     open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n";
2245     for my $key (keys %interesting_packages) {
2246         my $pkg = get_source_info($key);
2247         print SOURCES "Package: $key\n";
2248         print SOURCES "Version: $pkg->{'version'}\n";
2249         print SOURCES "Build-Depends: $srcs->{$key}{'dep'}\n" if $srcs->{$key}{'dep'};
2250         print SOURCES "Build-Conflicts: $srcs->{$key}{'conf'}\n" if $srcs->{$key}{'conf'};
2251         print SOURCES "Architecture: all\n";
2252         print SOURCES "\n";
2253     }
2254     close SOURCES;
2255
2256     if (open(EDOS,"-|","wb-edos-builddebcheck", "-a", $arch, $packagesfile, $tmpfile))
2257     {
2258         local($/) = ""; # read in paragraph mode
2259         while( <EDOS> ) {
2260                 my( $key, $reason ) ;
2261                 s/\s*$//m;
2262                 /^Package:\s*(\S+)$/mi and $key = $1;
2263                 /^Failed-Why:(([^\n]|\n ([^\n]|\.))*)$/msi and $reason = $1;
2264                 $reason =~ s/^\s*//mg;
2265                 $reason ||= 'No reason given by edos-debcheck';
2266
2267                 if (exists $interesting_packages{$key}) {
2268                     $interesting_packages{$key} = $reason;
2269                 } else {
2270                     #print "TODO: edos reported a package we do not care about now\n" if $verbose;
2271                 }
2272         }
2273         close EDOS;
2274     } else {
2275         print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" .
2276               "all packages have installable build-dependencies."
2277     }
2278     
2279     unlink( $tmpfile );
2280
2281     for my $key (keys %interesting_packages) {
2282         my $pkg = get_source_info($key);
2283         my $change = 
2284             (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
2285             (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
2286         my $problemchange = $interesting_packages{$key} ne $pkg->{'bd_problem'};
2287         if ($change) {
2288             if (defined $interesting_packages{$key}) {
2289                     change_state( \$pkg, 'BD-Uninstallable' );
2290                     $pkg->{'bd_problem'} = $interesting_packages{$key};
2291             } else {
2292                     change_state( \$pkg, 'Needs-Build' );
2293             }
2294         }
2295         if ($problemchange) {
2296             if (defined $interesting_packages{$key}) {
2297                     $pkg->{'bd_problem'} = $interesting_packages{$key};
2298             }   
2299         }
2300         if ($change) {
2301             log_ta( $pkg, "--merge-all" );
2302             print "edos-builddebchange changed state of ${key}_$pkg->{'version'} to $pkg->{'state'}\n" if $verbose;
2303         }
2304         if ($change || $problemchange) {
2305             update_source_info($pkg);
2306         }
2307     }
2308 }
2309
2310 sub usage {
2311         my $prgname;
2312         ($prgname = $0) =~ s,^.*/,,;
2313         print <<"EOF";
2314 Usage: $prgname <options...> <package_version...>
2315 Options:
2316     -v, --verbose: Verbose execution.
2317     --take: Take package for building [default operation]
2318     -f, --failed: Record in database that a build failed due to
2319         deficiencies in the package (that aren't fixable without a new
2320         source version).
2321     -u, --uploaded: Record in the database that the packages build
2322         correctly and were uploaded.
2323     -n, --no-build: Record in the database that the packages aren't
2324         desired for this architecture and shouldn't appear in listings even
2325         if they're out of date.
2326     --dep-wait: Record in the database that the packages are waiting
2327         for some source dependencies to become available
2328     --binNMU num: Schedule a re-build of the package with unchanged source, but
2329          a new version number (source-version + "+b<num>")
2330     --give-back: Mark a package as ready to build that is in state Building,
2331          Built or Build-Attempted. To give back a package in state Failed, use
2332          --override. This command will actually put the package in state
2333          BD-Uninstallable, until the installability of its Build-Dependencies
2334          were verified. This happens at each call of --merge-all, usually
2335          every 15 minutes.
2336     --merge-quinn: Merge quinn-diff output into database.
2337     --merge-packages: Merge Packages files into database.
2338     --pretend-avail: Pretend that given packages are available now and give
2339         free packages waiting for them
2340     -i SRC_PKG, --info SRC_PKG: Show information for source package
2341     -l STATE, --list=STATE: List all packages in state STATE; can be
2342         combined with -U to restrict to a specific user; STATE can
2343         also be 'all'
2344     -m MESSAGE, --message=MESSAGE: Give reason why package failed or
2345         source dependency list
2346         (used with -f, --dep-wait, and --binNMU)
2347     -o, --override: Override another user's lock on a package, i.e.
2348         take it over; a notice mail will be sent to the other user
2349     -U USER, --user=USER: select user name for which listings should
2350         apply, if not given all users are listed.
2351         if -l is missing, set user name to be entered in db; usually
2352         automatically choosen
2353     --import FILE: Import database from a ASCII file FILE
2354
2355 The remaining arguments (depending on operation) usually start with
2356 "name_version", the trailer is ignored. This allows to pass the names
2357 of .dsc files, for which file name completion can be used.
2358 --merge-packages and --merge-quinn take Package/quin--diff file names
2359 on the command line or read stdin. --list needs nothing more on the
2360 command line. --info takes source package names (without version).
2361 EOF
2362         exit 1;
2363 }
2364
2365 sub pkg_version_eq {
2366         my $pkg = shift;
2367         my $version = shift;
2368
2369         return 1
2370                if (defined $pkg->{'binary_nmu_version'}) and 
2371                version_compare(binNMU_version($pkg->{'version'},
2372                         $pkg->{'binary_nmu_version'}),'=', $version);
2373         return version_compare( $pkg->{'version'}, "=", $version );
2374 }
2375
2376 sub table_name {
2377         return $arch;
2378 }
2379
2380 sub user_table_name {
2381         return $arch . '_users';
2382 }
2383
2384 sub get_source_info {
2385         my $name = shift;
2386         my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' . 
2387                 table_name() . ' WHERE package = ? AND distribution = ?',
2388                 undef, $name, $distribution);
2389         return $pkg;
2390 }
2391
2392 sub get_all_source_info {
2393         my $db = $dbh->selectall_hashref('SELECT * FROM ' . table_name() .
2394                 ' WHERE distribution = ?',
2395                 'package', undef, $distribution);
2396         return $db;
2397 }
2398
2399 sub update_source_info {
2400         my $pkg = shift;
2401
2402         my $pkg2 = get_source_info($pkg->{'package'});
2403         if (! defined $pkg2)
2404         {
2405                 add_source_info($pkg);
2406         }
2407
2408         $dbh->do('UPDATE ' . table_name() . ' SET ' .
2409                         'version = ?, ' .
2410                         'state = ?, ' .
2411                         'section = ?, ' .
2412                         'priority = ?, ' .
2413                         'installed_version = ?, ' .
2414                         'previous_state = ?, ' .
2415                         'state_change = ?, ' .
2416                         'notes = ?, ' .
2417                         'builder = ?, ' .
2418                         'failed = ?, ' .
2419                         'old_failed = ?, ' .
2420                         'binary_nmu_version = ?, ' .
2421                         'binary_nmu_changelog = ?, ' .
2422                         'failed_category = ?, ' .
2423                         'permbuildpri = ?, ' .
2424                         'buildpri = ?, ' .
2425                         'depends = ?, ' .
2426                         'rel = ?, ' .
2427                         'bd_problem = ? ' .
2428                         'WHERE package = ? AND distribution = ?',
2429                 undef,
2430                 $pkg->{'version'},
2431                 $pkg->{'state'},
2432                 $pkg->{'section'},
2433                 $pkg->{'priority'},
2434                 $pkg->{'installed_version'},
2435                 $pkg->{'previous_state'},
2436                 $pkg->{'state_change'},
2437                 $pkg->{'notes'},
2438                 $pkg->{'builder'},
2439                 $pkg->{'failed'},
2440                 $pkg->{'old_failed'},
2441                 $pkg->{'binary_nmu_version'},
2442                 $pkg->{'binary_nmu_changelog'},
2443                 $pkg->{'failed_category'},
2444                 $pkg->{'permbuildpri'},
2445                 $pkg->{'buildpri'},
2446                 $pkg->{'depends'},
2447                 $pkg->{'rel'},
2448                 $pkg->{'bd_problem'},
2449                 $pkg->{'package'},
2450                 $distribution) or die $dbh->errstr;
2451 }
2452
2453 sub add_source_info {
2454         my $pkg = shift;
2455         $dbh->do('INSERT INTO ' . table_name() .
2456                         ' (package, distribution) values (?, ?)',
2457                 undef, $pkg->{'package'}, $distribution) or die $dbh->errstr;
2458 }
2459
2460 sub del_source_info {
2461         my $name = shift;
2462         $dbh->do('DELETE FROM ' . table_name() .
2463                         ' WHERE package = ? AND distribution = ?',
2464                 undef, $name, $distribution) or die $dbh->errstr;
2465 }
2466
2467 sub get_user_info {
2468         my $name = shift;
2469         my $user = $dbh->selectrow_hashref('SELECT * FROM ' . 
2470                 user_table_name() . ' WHERE username = ? AND distribution = ?',
2471                 undef, $name, $distribution);
2472         return $user;
2473 }
2474
2475 sub update_user_info {
2476         my $user = shift;
2477         $dbh->do('UPDATE ' . user_table_name() .
2478                         ' SET last_seen = now() WHERE username = ?' .
2479                         ' AND distribution = ?',
2480                 undef, $user, $distribution)
2481                 or die $dbh->errstr;
2482 }
2483
2484
2485 sub add_user_info {
2486         my $user = shift;
2487         $dbh->do('INSERT INTO ' . user_table_name() .
2488                         ' (username, distribution, last_seen)' .
2489                         ' values (?, ?, now())',
2490                 undef, $user, $distribution)
2491                 or die $dbh->errstr;
2492 }
2493