2 # $Id: errorlib.in,v 1.40 2003/08/30 00:15:15 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',
90 my ($lref, $location) = @_;
91 my $status = getbugcomponent($lref, 'summary', $location);
92 return undef unless defined $status;
93 if (!open(S,$status)) { return undef; }
103 $version = $1 if /^Format-Version: ([0-9]+)/i;
106 # Version 2 is the latest format version currently supported.
107 return undef if $version > 2;
109 my %namemap = reverse %fields;
110 for my $line (@lines) {
111 if ($line =~ /(\S+?): (.*)/) {
112 my ($name, $value) = (lc $1, $2);
113 $data{$namemap{$name}} = $value if exists $namemap{$name};
116 for my $field (keys %fields) {
117 $data{$field} = '' unless exists $data{$field};
122 $data{severity} = 'normal' if $data{severity} eq '';
128 local ($lref, $location) = @_;
129 &filelock("lock/$lref");
130 my $data = readbug($lref, $location);
131 &unfilelock unless defined $data;
138 $version = 2 unless defined $version;
143 for my $field (@v1fieldorder) {
144 if (exists $data->{$field}) {
145 $contents .= "$data->{$field}\n";
150 } elsif ($version == 2) {
151 # Version 2. Add a file format version number for the sake of
152 # further extensibility in the future.
153 $contents .= "Format-Version: 2\n";
154 for my $field (keys %fields) {
155 if (exists $data->{$field} and $data->{$field} ne '') {
156 # Output field names in proper case, e.g. 'Merged-With'.
157 my $properfield = $fields{$field};
158 $properfield =~ s/(?:^|(?<=-))([a-z])/\u$1/g;
159 $contents .= "$properfield: $data->{$field}\n";
168 my ($ref, $data, $location, $minversion, $disablebughook) = @_;
171 my %outputs = (1 => 'status', 2 => 'summary');
172 for my $version (keys %outputs) {
173 next if defined $minversion and $version < $minversion;
174 my $status = getbugcomponent($ref, $outputs{$version}, $location);
175 &quit("can't find location for $ref") unless defined $status;
176 open(S,"> $status.new") || &quit("opening $status.new: $!");
177 print(S makestatus($data, $version)) ||
178 &quit("writing $status.new: $!");
179 close(S) || &quit("closing $status.new: $!");
185 rename("$status.new",$status) || &quit("installing new $status: $!");
188 # $disablebughook is a bit of a hack to let format migration scripts use
189 # this function rather than having to duplicate it themselves.
190 &bughook($change,$ref,$data) unless $disablebughook;
199 # NB - NOT COMPATIBLE WITH `with-lock'
200 local ($lockfile,$flockpushno,$evalstring,$count,$errors,@s1,@s2) = @_;
201 $flockpushno= $#filelocks+1;
202 $count= 10; $errors= '';
205 open(FLOCK${flockpushno},\"> \$lockfile\") || die \"open: \$!\";
206 \$flockwant= pack(\$flockstruct,&F_WRLCK,0,0,1,0);".
208 fcntl(FLOCK$flockpushno,&F_SETLK,\$flockwant) || die \"setlk: \$!\";" : "
209 \$z= syscall(&SYS_fcntl,fileno(FLOCK$flockpushno),&F_SETLK,\$flockwant) < 0
210 && die \"syscall fcntl setlk: \$!\";") ."
211 (\@s1= lstat(\$lockfile)) || die \"lstat: \$!\";
212 (\@s2= stat(FLOCK$flockpushno)) || die \"fstat: \$!\";
213 join(',',\@s1) eq join(',',\@s2) || die \"file switched\";
216 last if eval $evalstring;
218 eval "close(FLOCK$flockpushno);";
221 &quit("failed to get lock on file $lockfile: $errors // $evalstring");
225 push(@cleanups,'unfilelock');
226 push(@filelocks,$lockfile);
230 if (@filelocks == 0) {
231 warn "unfilelock called with no active filelocks!\n";
234 local ($lockfile) = pop(@filelocks);
236 eval 'close(FLOCK'.($#filelocks+1).');' || warn "failed to close lock file $lockfile: $!";
237 unlink($lockfile) || warn "failed to remove lock file $lockfile: $!";
241 print DEBUG "quitting >$_[0]<\n";
243 while ($u= $cleanups[$#cleanups]) { &$u; }
247 %saniarray= ('<','lt', '>','gt', '&','amp', '"','quot');
252 while ($in =~ m/[<>&"]/) {
253 $out.= $`. '&'. $saniarray{$&}. ';';
260 sub update_realtime {
261 my ($file, $bug, $new) = @_;
263 # update realtime index.db
265 open(IDXDB, "<$file") or die "Couldn't open $file";
266 open(IDXNEW, ">$file.new");
270 while($line = <IDXDB>) {
271 @line = split /\s/, $line;
272 last if ($line[1] >= $bug);
277 if ($new eq "NOCHANGE") {
278 print IDXNEW $line if ($line ne "" && $line[1] == $ref);
279 } elsif ($new eq "REMOVE") {
284 if ($line ne "" && $line[1] > $bug) {
289 print IDXNEW while(<IDXDB>);
294 rename("$file.new", $file);
299 sub bughook_archive {
301 &filelock("debbugs.trace.lock");
302 &appendfile("debbugs.trace","archive $ref\n");
303 my $line = update_realtime(
304 "$gSpoolDir/index.db.realtime",
307 update_realtime("$gSpoolDir/index.archive.realtime",
313 my ( $type, $ref, $data ) = @_;
314 &filelock("debbugs.trace.lock");
316 &appendfile("debbugs.trace","$type $ref\n",makestatus($data, 1));
318 my $whendone = "open";
319 my $severity = $gDefaultSeverity;
320 (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g;
323 $whendone = "forwarded" if length $data->{forwarded};
324 $whendone = "done" if length $data->{done};
325 $severity = $data->{severity} if length $data->{severity};
327 my $k = sprintf "%s %d %d %s [%s] %s %s\n",
328 $pkglist, $ref, $data->{date}, $whendone,
329 $data->{originator}, $severity, $data->{keywords};
331 update_realtime("$gSpoolDir/index.db.realtime", $ref, $k);
338 if (!open(AP,">>$file")) {
339 print DEBUG "failed open log<\n";
340 print DEBUG "failed open log err $!<\n";
341 &quit("opening $file (appendfile): $!");
343 print(AP @_) || &quit("writing $file (appendfile): $!");
344 close(AP) || &quit("closing $file (appendfile): $!");
349 my $type = $entity->effective_type;
350 if ($type eq 'text/plain' or
351 ($type =~ m#text/# and $type ne 'text/html') or
352 $type eq 'application/pgp') {
353 return $entity->bodyhandle;
354 } elsif ($type eq 'multipart/alternative') {
355 # RFC 2046 says we should use the last part we recognize.
356 for my $part (reverse $entity->parts) {
357 my $ret = getmailbody($part);
361 # For other multipart types, we just pretend they're
362 # multipart/mixed and run through in order.
363 for my $part ($entity->parts) {
364 my $ret = getmailbody($part);
373 map { $_->address() }
374 map { Mail::Address->parse($_) } @_;
379 map { s/^([\01-\07\030])/\030$1/gm } @log;
383 sub isstrongseverity {
384 my $severity = shift;
385 $severity = $gDefaultSeverity if $severity eq '';
386 return grep { $_ eq $severity } @gStrongSeverities;
390 @severities= grep { not exists $gObsoleteSeverities{$_} } @gSeverityList;
391 @showseverities= @severities;
392 grep ($_= $_ eq '' ? $gDefaultSeverity : $_, @showseverities);
393 %displayshowseverities= %gSeverityDisplay;
396 if (defined $gFowardList and not defined $gForwardList) {
397 $gForwardList = $gFowardList;