2 package Debbugs::Common;
6 Debbugs::Common -- Common routines for all of Debbugs
10 use Debbugs::Common qw(:url :html);
15 This module is a replacement for the general parts of errorlib.pl.
16 subroutines in errorlib.pl will be gradually phased out and replaced
17 with equivalent (or better) functionality here.
25 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
26 use base qw(Exporter);
30 $DEBUG = 0 unless defined $DEBUG;
33 %EXPORT_TAGS = (util => [qw(getbugcomponent getbuglocation getlocationpath get_hashname),
34 qw(appendfile buglog getparsedaddrs getmaintainers),
35 qw(getmaintainers_reverse)
38 lock => [qw(filelock unfilelock)],
41 Exporter::export_ok_tags(qw(lock quit util));
42 $EXPORT_TAGS{all} = [@EXPORT_OK];
45 #use Debbugs::Config qw(:globals);
46 use Debbugs::Config qw(:config);
48 use Debbugs::MIME qw(decode_rfc1522);
53 our $DEBUG_FH = \*STDERR if not defined $DEBUG_FH;
57 The following functions are exported by the C<:util> tag
59 =head2 getbugcomponent
61 my $file = getbugcomponent($bug_number,$extension,$location)
63 Returns the path to the bug file in location C<$location>, bug number
64 C<$bugnumber> and extension C<$extension>
69 my ($bugnum, $ext, $location) = @_;
71 if (not defined $location) {
72 $location = getbuglocation($bugnum, $ext);
73 # Default to non-archived bugs only for now; CGI scripts want
74 # archived bugs but most of the backend scripts don't. For now,
75 # anything that is prepared to accept archived bugs should call
76 # getbuglocation() directly first.
77 return undef if defined $location and
78 ($location ne 'db' and $location ne 'db-h');
80 my $dir = getlocationpath($location);
81 return undef if not defined $dir;
82 if (defined $location and $location eq 'db') {
83 return "$dir/$bugnum.$ext";
85 my $hash = get_hashname($bugnum);
86 return "$dir/$hash/$bugnum.$ext";
92 getbuglocation($bug_number,$extension)
94 Returns the the location in which a particular bug exists; valid
95 locations returned currently are archive, db-h, or db. If the bug does
96 not exist, returns undef.
101 my ($bugnum, $ext) = @_;
102 my $archdir = get_hashname($bugnum);
103 return 'archive' if -r getlocationpath('archive')."/$archdir/$bugnum.$ext";
104 return 'db-h' if -r getlocationpath('db-h')."/$archdir/$bugnum.$ext";
105 return 'db' if -r getlocationpath('db')."/$bugnum.$ext";
110 =head2 getlocationpath
112 getlocationpath($location)
114 Returns the path to a specific location
118 sub getlocationpath {
120 if (defined $location and $location eq 'archive') {
121 return "$config{spool_dir}/archive";
122 } elsif (defined $location and $location eq 'db') {
123 return "$config{spool_dir}/db";
125 return "$config{spool_dir}/db-h";
134 Returns the hash of the bug which is the location within the archive
139 return "" if ( $_[ 0 ] < 0 );
140 return sprintf "%02d", $_[ 0 ] % 100;
147 Returns the path to the logfile corresponding to the bug.
153 my $location = getbuglocation($bugnum, 'log');
154 return getbugcomponent($bugnum, 'log', $location) if ($location);
155 $location = getbuglocation($bugnum, 'log.gz');
156 return getbugcomponent($bugnum, 'log.gz', $location);
162 appendfile($file,'data','to','append');
164 Opens a file for appending and writes data to it.
170 if (!open(AP,">>$file")) {
171 print $DEBUG_FH "failed open log<\n" if $DEBUG;
172 print $DEBUG_FH "failed open log err $!<\n" if $DEBUG;
173 &quit("opening $file (appendfile): $!");
175 print(AP @_) || &quit("writing $file (appendfile): $!");
176 close(AP) || &quit("closing $file (appendfile): $!");
179 =head2 getparsedaddrs
181 my $address = getparsedaddrs($address);
182 my @address = getpasredaddrs($address);
184 Returns the output from Mail::Address->parse, or the cached output if
185 this address has been parsed before. In SCALAR context returns the
186 first address parsed.
194 return () unless defined $addr;
195 return wantarray?@{$_parsedaddrs{$addr}}:$_parsedaddrs{$addr}[0]
196 if exists $_parsedaddrs{$addr};
197 @{$_parsedaddrs{$addr}} = Mail::Address->parse($addr);
198 return wantarray?@{$_parsedaddrs{$addr}}:$_parsedaddrs{$addr}[0];
204 return $_maintainer if $_maintainer;
207 for my $file (@config{qw(maintainer_file maintainer_file_override)}) {
208 next unless defined $file;
209 my $maintfile = new IO::File $file,'r' or
210 &quitcgi("Unable to open $file: $!");
211 while(<$maintfile>) {
212 next unless m/^(\S+)\s+(\S.*\S)\s*$/;
216 for my $maint (map {lc($_->address)} getparsedaddrs($b)) {
217 push @{$maintainer_rev{$maint}},$a;
222 $_maintainer = \%maintainer;
223 $_maintainer_rev = \%maintainer_rev;
226 sub getmaintainers_reverse{
227 return $_maintainer_rev if $_maintainer_rev;
229 return $_maintainer_rev;
235 These functions are exported with the :lock tag
241 FLOCKs the passed file. Use unfilelock to unlock it.
249 # NB - NOT COMPATIBLE WITH `with-lock'
251 my ($count,$errors) = @_;
252 $count= 10; $errors= '';
255 my $fh2 = IO::File->new($lockfile,'w')
256 or die "Unable to open $lockfile for writing: $!";
257 flock($fh2,LOCK_EX|LOCK_NB)
258 or die "Unable to lock $lockfile $!";
265 push @filelocks, {fh => $fh, file => $lockfile};
270 &quit("failed to get lock on $lockfile -- $errors");
274 push(@cleanups,\&unfilelock);
282 Unlocks the file most recently locked.
284 Note that it is not currently possible to unlock a specific file
285 locked with filelock.
290 if (@filelocks == 0) {
291 warn "unfilelock called with no active filelocks!\n";
294 my %fl = %{pop(@filelocks)};
296 flock($fl{fh},LOCK_UN)
297 or warn "Unable to unlock lockfile $fl{file}: $!";
299 or warn "Unable to close lockfile $fl{file}: $!";
301 or warn "Unable to unlink locfile $fl{file}: $!";
308 These functions are exported with the :quit tag.
314 Exits the program by calling die after running some cleanups.
316 This should be replaced with an END handler which runs the cleanups
317 instead. (Or possibly a die handler, if the cleanups are important)
322 print $DEBUG_FH "quitting >$_[0]<\n" if $DEBUG;
324 while ($u= $cleanups[$#cleanups]) { &$u; }