2 # Freshmeat.pl: Frontend to www.freshmeat.net
4 # Version: v0.7d (20000923)
11 use vars qw(@cols @data $string %pkg $i $locktime);
14 'public' => 'http://www.freshmeat.net/backend/fm-projects.rdf.bz2',
15 # 'private' => 'http://feed.freshmeat.net/appindex/appindex.txt',
19 # Usage: &Freshmeat($string);
22 my $refresh = &::getChanConfDefault("freshmeatRefreshInterval",
23 "", 24) * 60 * 60 * 7;
25 my $last_refresh = &::dbGet("freshmeat", "latest_version", "projectname_short='_'");
28 if (defined $last_refresh and $last_refresh =~ /^\d+$/) {
29 $renewtable++ if (time() - $last_refresh > $refresh);
33 $renewtable++ if (&::countKeys("freshmeat") < 1000);
36 if ($$ == $::bot_pid) {
37 &::Forker("freshmeat", sub {
41 # both parent/fork runs here, in case the following looks weird.
46 return if ($$ == $::bot_pid);
49 if (!&showPackage($sstr)) { # no exact match.
50 my $start_time = &::timeget();
53 # search by key/NAME first.
54 foreach (&::searchTable("freshmeat", "projectname_short", "projectname_short",$sstr)) {
55 $hash{$_} = 1 unless exists $hash{$_};
58 # search by description line.
59 foreach (&::searchTable("freshmeat", "projectname_short", "desc_short", $sstr)) {
60 $hash{$_} = 1 unless exists $hash{$_};
61 last if (scalar keys %hash > 15);
64 my @list = keys %hash;
65 # search by value, if we have enough room to do it.
66 if (scalar @list == 1) {
67 &::status("only one match found; showing full info.");
68 &showPackage($list[0]);
72 # show how long it took.
73 my $delta_time = &::timedelta($start_time);
74 &::status(sprintf("freshmeat: %.02f sec to complete query.", $delta_time)) if ($delta_time > 0);
78 s/([\,\;]+)/\037$1\037/g;
81 &::performStrictReply( &::formListReply(1, "Freshmeat ", @list) );
87 my @fm = &::dbGet("freshmeat", "*", "projectname_short='$pkg'");
89 if (scalar @fm) { #1: perfect match of name.
91 $retval = "$fm[0] \002(\002$fm[5]\002)\002, ";
92 # $retval .= "section $fm[3], ";
93 $retval .= "is $fm[2]. ";
94 $retval .= "Version: \002$fm[1]\002, ";
95 # $retval .= "Development: \002$fm[2]\002. ";
98 # $retval .= " deb: ".$fm[3] if ($fm[3] ne ""); # 'deb'.
99 &::performStrictReply($retval);
107 my @fm = &::randKey("freshmeat","*");
109 if (scalar @fm) { #1: perfect match of name.
111 $retval = "$fm[0] \002(\002$fm[5]\002)\002, ";
112 # $retval .= "section $fm[3], ";
113 $retval .= "is $fm[2]. ";
114 $retval .= "Version: \002$fm[1]\002, ";
115 # $retval .= "Development: \002$fm[2]\002. ";
118 # $retval .= " deb: ".$fm[3] if ($fm[3] ne ""); # 'deb'.
127 my $start_time = &::timeget(); # set the start time.
128 my $idx = "$::param{tempDir}/fm-projects.rdf.bz2";
130 if (!&::loadPerlModule("XML::Parser")) {
131 &::WARN("don't have xml::parser...");
134 my $p = new XML::Parser(Style => 'Objects');
143 &::msg($::who, "Updating freshmeat index... please wait");
145 if (&::isStale($idx, 1)) {
146 &::status("Freshmeat: fetching data.");
148 foreach (keys %urls) {
149 $urls{$_} =~ /^.*\/(.*)$/;
150 $idx = "$::param{tempDir}/$1";
151 my $retval = &::getURLAsFile($urls{$_}, $idx);
152 next if ($retval =~ /^(403|500)$/);
154 &::DEBUG("FM: last! retval => '$retval'.");
158 &::status("Freshmeat: local file hack.");
162 &::msg($::who, "the freshmeat butcher is closed.");
166 if ( -s $idx < 100000) {
167 &::DEBUG("FM: index too small?");
169 &::msg($::who, "internal error?");
173 if ($idx =~ /bz2$/) {
174 open(IN, "bzcat $idx |");
175 } elsif ($idx =~ /gz$/) {
176 open(IN, "gzcat $idx |");
181 # delete the table before we redo it.
182 &::deleteTable("freshmeat");
184 ### lets get on with business.
185 # set the last refresh time. fixes multiple spawn bug.
186 &::dbSet("freshmeat",
187 { "projectname_short" => "_" },
188 { "latest_version" => time() }
191 &::dbRaw("LOCK", "LOCK TABLES freshmeat WRITE");
192 @cols = &::dbGetColInfo("freshmeat");
196 # this mess is to not dump IN to memory.
207 next unless (/<\/project>/);
209 # XML::Parser's parse() doesn't like the following.
210 # but parsefile() does... why!
225 s/\cN//g; # fucking openbsd morons.
230 s/\cM/ /g; # stupid windows morons
241 if (0 and $str =~ s/\&(\S+?);//g) {
242 &::DEBUG("fm: sarred $1 to ''.");
245 $p->parse($str, ProtocolEncoding => 'ISO-8859-1');
250 &::dbRaw("UNLOCK", "UNLOCK TABLES");
252 my $delta_time = &::timedelta($start_time);
253 &::status(sprintf("Freshmeat: %.02f sec to complete.", $delta_time)) if ($delta_time > 0);
255 my $count = &::countKeys("freshmeat");
256 &::status("Freshmeat: $count entries loaded.");
259 sub freshmeatAnnounce {
260 my $file = "$::param{tempDir}/fm_recent.txt";
263 ### if file exists, lets read it.
273 my @array = &::getURL("http://core.freshmeat.net/backend/recentnews.txt");
277 my($what,$date,$url) = splice(@array,0,3);
281 ### if file does not exist, write new.
293 for(my $i=0; $i<scalar(@old); $i++) {
294 last if ($now[$i] eq $old[0]);
295 push(@new, $now[$i]);
299 &::DEBUG("fA: no new items.");
310 return "Freshmeat update: ".join(" \002::\002 ", @new);
316 return if ($t =~ /^\s*$/);
322 my($expat,$text) = @_;
324 $pkg{$text} = $string;
326 if ($expat->depth == 0) {
327 for (my $j=0; $j<scalar @cols; $j++) {
328 $data[$j] = $pkg{ $cols[$j] };
332 &::dbSetRow("freshmeat", @data);
336 if ($i % 200 == 0 and $i != 0) {
338 &::status("FM: unlocking and locking ($i): ".
339 &::Time2String( time() - $locktime ) );
342 # I think the following leaks 120k of memory each time it's
343 # called... the wonders of libmysql-perl leaking!
345 &::dbRaw("UNLOCK", "UNLOCK TABLES");
346 ### another lame hack to "prevent" errors.
347 select(undef, undef, undef, 0.2);
348 &::dbRaw("LOCK", "LOCK TABLES freshmeat WRITE");