2 # $Id: errorlib.in,v 1.37 2003/08/23 13:50:45 cjwatson Exp $
6 sub F_SETLK { 6; } sub F_WRLCK{ 1; }
7 $flockstruct= 'sslll'; # And there ought to be something for this too.
10 return "" if ( $_[ 0 ] < 0 );
11 return sprintf "%02d", $_[ 0 ] % 100;
14 sub unlockreadbugmerge {
16 &unfilelock if $rv >= 2;
17 &unfilelock if $rv >= 1;
20 sub lockreadbugmerge {
21 local ($lref, $location) = @_;
23 if (!($data = &lockreadbug($lref, $location))) { return ( 0, undef ); }
24 if (!length($data{mergedwith})) { return ( 1, $data ); }
26 &filelock('lock/merge');
27 if (!&lockreadbug($lref, $location)) { &unfilelock; return ( 0, undef ); }
32 my ( $bugnum, $ext ) = @_;
33 my $archdir = sprintf "%02d", $bugnum % 100;
34 return 'archive' if ( -r "$gSpoolDir/archive/$archdir/$bugnum.$ext" );
35 return 'db-h' if ( -r "$gSpoolDir/db-h/$archdir/$bugnum.$ext" );
36 return 'db' if ( -r "$gSpoolDir/db/$bugnum.$ext" );
42 if ($location eq 'archive') {
43 return "$gSpoolDir/archive";
44 } elsif ($location eq 'db') {
45 return "$gSpoolDir/db";
47 return "$gSpoolDir/db-h";
52 my ($bugnum, $ext, $location) = @_;
54 unless (defined $location) {
55 $location = getbuglocation($bugnum, $ext);
56 # Default to non-archived bugs only for now; CGI scripts want
57 # archived bugs but most of the backend scripts don't. For now,
58 # anything that is prepared to accept archived bugs should call
59 # getbuglocation() directly first.
60 return undef if defined $location and
61 ($location ne 'db' and $location ne 'db-h');
63 my $dir = getlocationpath($location);
64 return undef unless $dir;
65 if ($location eq 'db') {
66 return "$dir/$bugnum.$ext";
68 my $hash = get_hashname($bugnum);
69 return "$dir/$hash/$bugnum.$ext";
73 my @v1fieldorder = qw(originator date subject msgid package
74 keywords done forwarded mergedwith severity);
76 my %fields = (originator => 'submitter',
79 msgid => 'message-id',
80 'package' => 'package',
83 forwarded => 'forwarded-to',
84 mergedwith => 'merged-with',
85 severity => 'severity',
89 my ($lref, $location) = @_;
90 my $status = getbugcomponent($lref, 'db', $location);
91 return undef unless defined $status;
92 if (!open(S,$status)) { return undef; }
102 $version = $1 if /^Format-Version: (.*)/i;
105 # Version 2 is the latest format version currently supported.
106 return undef if $version > 2;
108 my %namemap = reverse %fields;
109 for my $line (@lines) {
110 if ($line =~ /(\S+?): (.*)/) {
111 my ($name, $value) = (lc $1, $2);
112 $data{$namemap{$name}} = $value if exists $namemap{$name};
115 for my $field (keys %fields) {
116 $data{$field} = '' unless exists $data{$field};
121 $data{severity} = 'normal' if $data{severity} eq '';
127 local ($lref, $location) = @_;
128 &filelock("lock/$lref");
129 my $data = readbug($lref, $location);
130 &unfilelock unless defined $data;
137 $version = 2 unless defined $version;
142 for my $field (@v1fieldorder) {
143 if (exists $data->{$field}) {
144 $contents .= "$data->{$field}\n";
149 } elsif ($version == 2) {
150 # Version 2. Add a file format version number for the sake of
151 # further extensibility in the future.
152 $contents .= "Format-Version: 2\n";
153 for my $field (keys %fields) {
154 if (exists $data->{$field} and $data->{$field} ne '') {
155 # Output field names in proper case, e.g. 'Merged-With'.
156 my $properfield = $fields{$field};
157 $properfield =~ s/(?:^|(?<=-))([a-z])/\u$1/g;
158 $contents .= "$properfield: $data->{$field}\n";
167 my ($ref, $data, $location, $minversion, $disablebughook) = @_;
170 my %outputs = (1 => 'status', 2 => 'db');
171 for my $version (keys %outputs) {
172 next if defined $minversion and $version < $minversion;
173 my $status = getbugcomponent($ref, $outputs{$version}, $location);
174 &quit("can't find location for $ref") unless defined $status;
175 open(S,"> $status.new") || &quit("opening $status.new: $!");
176 print(S makestatus($data, $version)) ||
177 &quit("writing $status.new: $!");
178 close(S) || &quit("closing $status.new: $!");
184 rename("$status.new",$status) || &quit("installing new $status: $!");
187 # $disablebughook is a bit of a hack to let format migration scripts use
188 # this function rather than having to duplicate it themselves.
189 &bughook($change,$ref,$data) unless $disablebughook;
198 # NB - NOT COMPATIBLE WITH `with-lock'
199 local ($lockfile,$flockpushno,$evalstring,$count,$errors,@s1,@s2) = @_;
200 $flockpushno= $#filelocks+1;
201 $count= 10; $errors= '';
204 open(FLOCK${flockpushno},\"> \$lockfile\") || die \"open: \$!\";
205 \$flockwant= pack(\$flockstruct,&F_WRLCK,0,0,1,0);".
207 fcntl(FLOCK$flockpushno,&F_SETLK,\$flockwant) || die \"setlk: \$!\";" : "
208 \$z= syscall(&SYS_fcntl,fileno(FLOCK$flockpushno),&F_SETLK,\$flockwant) < 0
209 && die \"syscall fcntl setlk: \$!\";") ."
210 (\@s1= lstat(\$lockfile)) || die \"lstat: \$!\";
211 (\@s2= stat(FLOCK$flockpushno)) || die \"fstat: \$!\";
212 join(',',\@s1) eq join(',',\@s2) || die \"file switched\";
215 last if eval $evalstring;
217 eval "close(FLOCK$flockpushno);";
220 &quit("failed to get lock on file $lockfile: $errors // $evalstring");
224 push(@cleanups,'unfilelock');
225 push(@filelocks,$lockfile);
229 if (@filelocks == 0) {
230 warn "unfilelock called with no active filelocks!\n";
233 local ($lockfile) = pop(@filelocks);
235 eval 'close(FLOCK'.($#filelocks+1).');' || warn "failed to close lock file $lockfile: $!";
236 unlink($lockfile) || warn "failed to remove lock file $lockfile: $!";
240 print DEBUG "quitting >$_[0]<\n";
242 while ($u= $cleanups[$#cleanups]) { &$u; }
246 %saniarray= ('<','lt', '>','gt', '&','amp', '"','quot');
251 while ($in =~ m/[<>&"]/) {
252 $out.= $`. '&'. $saniarray{$&}. ';';
259 sub update_realtime {
260 my ($file, $bug, $new) = @_;
262 # update realtime index.db
264 open(IDXDB, "<$file") or die "Couldn't open $file";
265 open(IDXNEW, ">$file.new");
269 while($line = <IDXDB>) {
270 @line = split /\s/, $line;
271 last if ($line[1] >= $bug);
276 if ($new eq "NOCHANGE") {
277 print IDXNEW $line if ($line ne "" && $line[1] == $ref);
278 } elsif ($new eq "REMOVE") {
283 if ($line ne "" && $line[1] > $bug) {
288 print IDXNEW while(<IDXDB>);
293 rename("$file.new", $file);
298 sub bughook_archive {
300 &filelock("debbugs.trace.lock");
301 &appendfile("debbugs.trace","archive $ref\n");
302 my $line = update_realtime(
303 "$gSpoolDir/index.db.realtime",
306 update_realtime("$gSpoolDir/index.archive.realtime",
312 my ( $type, $ref, $data ) = @_;
313 &filelock("debbugs.trace.lock");
315 &appendfile("debbugs.trace","$type $ref\n",makestatus($data, 1));
317 my $whendone = "open";
318 my $severity = $gDefaultSeverity;
319 (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g;
322 $whendone = "forwarded" if length $data->{forwarded};
323 $whendone = "done" if length $data->{done};
324 $severity = $data->{severity} if length $data->{severity};
326 my $k = sprintf "%s %d %d %s [%s] %s %s\n",
327 $pkglist, $ref, $data->{date}, $whendone,
328 $data->{originator}, $severity, $data->{keywords};
330 update_realtime("$gSpoolDir/index.db.realtime", $ref, $k);
337 if (!open(AP,">>$file")) {
338 print DEBUG "failed open log<\n";
339 print DEBUG "failed open log err $!<\n";
340 &quit("opening $file (appendfile): $!");
342 print(AP @_) || &quit("writing $file (appendfile): $!");
343 close(AP) || &quit("closing $file (appendfile): $!");
348 my $type = $entity->effective_type;
349 if ($type eq 'text/plain' or
350 ($type =~ m#text/# and $type ne 'text/html') or
351 $type eq 'application/pgp') {
352 return $entity->bodyhandle;
353 } elsif ($type eq 'multipart/alternative') {
354 # RFC 2046 says we should use the last part we recognize.
355 for my $part (reverse $entity->parts) {
356 my $ret = getmailbody($part);
360 # For other multipart types, we just pretend they're
361 # multipart/mixed and run through in order.
362 for my $part ($entity->parts) {
363 my $ret = getmailbody($part);
372 map { $_->address() }
373 map { Mail::Address->parse($_) } @_;
378 map { s/^([\01-\07\030])/\030$1/gm } @log;
382 sub isstrongseverity {
383 my $severity = shift;
384 $severity = $gDefaultSeverity if $severity eq '';
385 return grep { $_ eq $severity } @gStrongSeverities;
389 @severities= grep { not exists $gObsoleteSeverities{$_} } @gSeverityList;
390 @showseverities= @severities;
391 grep ($_= $_ eq '' ? $gDefaultSeverity : $_, @showseverities);
392 %displayshowseverities= %gSeverityDisplay;
395 if (defined $gFowardList and not defined $gForwardList) {
396 $gForwardList = $gFowardList;