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", "*",
88 "projectname_short=".&::dbQuote($pkg) );
90 if (scalar @fm) { #1: perfect match of name.
92 $retval = "$fm[0] \002(\002$fm[5]\002)\002, ";
93 # $retval .= "section $fm[3], ";
94 $retval .= "is $fm[2]. ";
95 $retval .= "Version: \002$fm[1]\002, ";
96 # $retval .= "Development: \002$fm[2]\002. ";
99 # $retval .= " deb: ".$fm[3] if ($fm[3] ne ""); # 'deb'.
100 &::performStrictReply($retval);
108 my @fm = &::randKey("freshmeat","*");
110 if (scalar @fm) { #1: perfect match of name.
112 $retval = "$fm[0] \002(\002$fm[5]\002)\002, ";
113 # $retval .= "section $fm[3], ";
114 $retval .= "is $fm[2]. ";
115 $retval .= "Version: \002$fm[1]\002, ";
116 # $retval .= "Development: \002$fm[2]\002. ";
119 # $retval .= " deb: ".$fm[3] if ($fm[3] ne ""); # 'deb'.
128 my $start_time = &::timeget(); # set the start time.
129 my $idx = "$::param{tempDir}/fm-projects.rdf.bz2";
131 if (!&::loadPerlModule("XML::Parser")) {
132 &::WARN("don't have xml::parser...");
135 my $p = new XML::Parser(Style => 'Objects');
144 &::msg($::who, "Updating freshmeat index... please wait");
146 if (&::isStale($idx, 1)) {
147 &::status("Freshmeat: fetching data.");
149 foreach (keys %urls) {
150 $urls{$_} =~ /^.*\/(.*)$/;
151 $idx = "$::param{tempDir}/$1";
152 my $retval = &::getURLAsFile($urls{$_}, $idx);
153 next if ($retval =~ /^(403|500)$/);
155 &::DEBUG("FM: last! retval => '$retval'.");
159 &::status("Freshmeat: local file hack.");
163 &::msg($::who, "the freshmeat butcher is closed.");
167 if ( -s $idx < 100000) {
168 &::DEBUG("FM: index too small?");
170 &::msg($::who, "internal error?");
174 if ($idx =~ /bz2$/) {
175 open(IN, "bzcat $idx |");
176 } elsif ($idx =~ /gz$/) {
177 open(IN, "gzcat $idx |");
182 # delete the table before we redo it.
183 &::deleteTable("freshmeat");
185 ### lets get on with business.
186 # set the last refresh time. fixes multiple spawn bug.
187 &::dbSet("freshmeat",
188 { "projectname_short" => "_" },
189 { "latest_version" => time(),
193 # &::dbRaw("LOCK", "LOCK TABLES freshmeat WRITE");
194 @cols = &::dbGetColInfo("freshmeat");
198 # this mess is to not dump IN to memory.
209 next unless (/<\/project>/);
211 # XML::Parser's parse() doesn't like the following.
212 # but parsefile() does... why!
227 s/\cN//g; # fucking openbsd morons.
232 s/\cM/ /g; # stupid windows morons
243 if (0 and $str =~ s/\&(\S+?);//g) {
244 &::DEBUG("fm: sarred $1 to ''.");
247 $p->parse($str, ProtocolEncoding => 'ISO-8859-1');
252 # &::dbRaw("UNLOCK", "UNLOCK TABLES");
254 my $delta_time = &::timedelta($start_time);
255 &::status(sprintf("Freshmeat: %.02f sec to complete.", $delta_time)) if ($delta_time > 0);
257 my $count = &::countKeys("freshmeat");
258 &::status("Freshmeat: $count entries loaded.");
261 sub freshmeatAnnounce {
262 my $file = "$::param{tempDir}/fm_recent.txt";
265 ### if file exists, lets read it.
275 my @array = &::getURL("http://core.freshmeat.net/backend/recentnews.txt");
279 my($what,$date,$url) = splice(@array,0,3);
283 ### if file does not exist, write new.
295 for(my $i=0; $i<scalar(@old); $i++) {
296 last if ($now[$i] eq $old[0]);
297 push(@new, $now[$i]);
301 &::DEBUG("fA: no new items.");
312 return "Freshmeat update: ".join(" \002::\002 ", @new);
318 return if ($t =~ /^\s*$/);
324 my($expat,$text) = @_;
326 $pkg{$text} = $string;
328 if ($expat->depth == 0) {
332 for (my $j=0; $j<scalar @cols; $j++) {
333 $data[$j] = $pkg{ $cols[$j] };
337 &::dbSetRow("freshmeat", [@data], "DELAY");
345 $data{$_} = $pkg{$_};
347 &::dbReplace("freshmeat", "projectname_short", %data);
353 if ($i % 200 == 0 and $i != 0) {
355 &::status("FM: unlocking and locking ($i): ".
356 &::Time2String( time() - $locktime ) );
359 # I think the following leaks 120k of memory each time it's
360 # called... the wonders of libmysql-perl leaking!
362 # &::dbRaw("UNLOCK", "UNLOCK TABLES");
363 ### another lame hack to "prevent" errors.
364 # select(undef, undef, undef, 0.2);
365 # &::dbRaw("LOCK", "LOCK TABLES freshmeat WRITE");