split development to it's own branch

This commit is contained in:
jonnyboy
2013-01-08 09:22:09 -05:00
parent bb0c0fb31b
commit c7232cd07c
26 changed files with 22 additions and 3674 deletions
+8 -10
View File
@@ -1,6 +1,6 @@
# SETUP
* These scripts were written and tested on Ubuntu 12.10 where bash is located at /bin/bash. You may need to create a symlink or edit these script accordingly.
* These scripts were written and tested on Ubuntu 12.10 where bash is located at /bin/bash. You may need to create a symlink or edit these scripts accordingly.
* Please backup your database first. Something like this should do it.
@@ -11,16 +11,16 @@
* If you decide to convert your database, I recommend using [kevin123's github](https://github.com/kevinlekiller/Newznab-Barracuda.git). I recommend only converting the binaries a parts table, using compressed tables. But, there are many choices. I suggest you read his README and follow his recommendations. Or, simply:
`cd /var/www/newznab/misc/testing/`
`cd /var/www/newznab/misc/testing`
`git clone https://github.com/kevinlekiller/Newznab-InnoDB-Dropin.git innodb`
`git clone https://github.com/kevinlekiller/Newznab-InnoDB-Dropin.git kev-innodb`
`cd innodb/lib/innodb`
`cd innodb/lib/kev-innodb`
`php convertToInnoDB.php`
* If, you have already converted your database, and didn't to the steps above. You will need to clone [kevin123's github](https://github.com/kevinlekiller/Newznab-InnoDB-Dropin.git) and get the scripts.
* Now, you will need to clone [andrewit's github](https://github.com/itandrew/Newznab-InnoDB-Dropin.git) and get his scripts.
`cd /var/www/newznab/misc/testing/`
@@ -33,10 +33,6 @@
`git clone https://github.com/jonnyboy/newznab-tmux.git tmux`
-or-
`git clone https://github.com/kevinlekiller/newznab-tmux tmux`
`cd tmux`
`nano edit_these.sh`
@@ -56,7 +52,7 @@
* If something looks stalled, it probably isn't. If all 13 panes are still there, it is most likely, as it should be.
* update_cleanup needs to be uncommented to actually do something, and update_parsing is good for fixing a few releases everytime it runs, not a silver bullet though
* **misc/testing/update_cleanup.php** needs to be edited to actually do something, and update_parsing is good for fixing a few releases everytime it runs, not a silver bullet though
* If you are running this on an OVH or kimsufi server, you may need to run sudo ./start.sh because they built grsecurity into the kernel.
@@ -64,6 +60,8 @@
* Join in the converstion at irc://moonlight.se.eu.synirc.net/newznab-tmux.
* The development branch of this git is still under heavy development.
* Thanks go to all who offered their assistance and improvement to these scripts.
<hr>
-14
View File
@@ -1,14 +0,0 @@
<?php
require_once("config.php");
require_once("lib/backfill.php");
if (isset($argv[1]))
$groupName = $argv[1];
else
$groupName = '';
$backfill = new Backfill();
$backfill->backfillAllGroups($groupName);
?>
-76
View File
@@ -1,76 +0,0 @@
<?php
require_once("config.php");
require_once(dirname(__FILE__)."/lib/groups.php");
require_once(dirname(__FILE__)."/lib/innodb/binaries.php");
require_once(dirname(__FILE__)."/lib/powerspawn.php");
$groups = new Groups;
$groupList = $groups->getActive();
unset($groups);
$ps = new PowerSpawn;
$ps->setCallback('psUpdateComplete');
$ps->maxChildren = 10;
$ps->timeLimit = 0; // Disable child timeout
echo "Starting threaded backfill process\n";
while ($ps->runParentCode())
{
// Start the parent loop
if (count($groupList))
{
// We still have groups to process
if ($ps->spawnReady())
{
// Spawn another thread
$ps->childData = array_pop($groupList);
echo "[Thread-MASTER] Spawning new thread. Still have " . count($groupList) ." group(s) to update after this\n";
$ps->spawnChild();
}
else
{
// There are no more slots available to run
$ps->tick();
#echo ". \n";
}
}
else
{
// No more groups to process
echo "No more groups to process - Initiating shutdown\n";
$ps->shutdown();
echo "Shutdown complete\n";
}
}
unset($groupList);
if ($ps->runChildCode())
{
$group = $ps->childData;
$thread = sprintf("%05d",$ps->myPID());
echo "[Thread-{$thread}] Begining backfill processing for group {$group['name']}\n";
$param = $group['name'];
$dir = dirname(__FILE__);
$file = 'backfill.php';
$output = shell_exec("php {$dir}/{$file} {$param}");
echo "[Thread-{$thread}] Completed update for group {$group['name']}\n";
}
// Exit to call back to parent - Let know that child has completed
exit(0);
// Create callback function
function psUpdateComplete()
{
echo "[Thread-MASTER] Threaded backfill process complete\n";
}
?>
-3
View File
@@ -1,3 +0,0 @@
<?php
require_once(dirname(__FILE__)."/../config.php");
?>
-300
View File
@@ -1,300 +0,0 @@
<?php
require_once(dirname(__FILE__)."/innodb/config.php");
require_once(dirname(__FILE__)."/framework/db.php");
require_once(dirname(__FILE__)."/groups.php");
require_once(dirname(__FILE__)."/site.php");
require_once(dirname(__FILE__)."/nntp.php");
require_once(dirname(__FILE__)."/innodb/binaries.php");
/**
* Retrieves messages from usenet based on provided backfill-to date.
*/
class Backfill
{
/**
* Default constructor.
*/
function Backfill($db = null)
{
if($db == null)
{
$this->db = new DB();
}
else
{
$this->db = $db;
}
$this->n = "\n";
}
/**
* Update all active groups categories and descriptions.
*/
function backfillAllGroups($groupName='', $backfillDate=null)
{
$n = $this->n;
$groups = new Groups;
$res = false;
if ($groupName != '')
{
$grp = $groups->getByName($groupName);
if ($grp)
$res = array($grp);
}
else
{
$res = $groups->getActive();
}
if ($res)
{
$nntp = new Nntp();
$nntp->doConnect();
foreach($res as $groupArr)
{
$this->backfillGroup($nntp, $groupArr, $backfillDate);
}
$nntp->doQuit();
}
else
{
echo "No groups specified. Ensure groups are added to newznab's database for updating.$n";
}
}
/**
* Update a group back to a specified date.
*/
function backfillGroup($nntp, $groupArr, $backfillDate=null)
{
$this->db->disableAutoCommit(); //Turn off auto committing
$binaries = new Binaries($this->db);
$n = $this->n;
$this->startGroup = microtime(true);
echo 'Processing '.$groupArr['name'].$n;
$data = $nntp->selectGroup($groupArr['name']);
if(PEAR::isError($data))
{
echo "Could not select group (bad name?): {$groupArr['name']}$n";
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return;
}
if ($backfillDate)
$targetpost = $this->daytopost($nntp,$groupArr['name'],$this->dateToDays($backfillDate),TRUE); // get targetpost based on date
else
$targetpost = $this->daytopost($nntp,$groupArr['name'],$groupArr['backfill_target'],TRUE); //get targetpost based on days target
if($groupArr['first_record'] == 0 || $groupArr['backfill_target'] == 0)
{
echo "Group ".$groupArr['name']." has invalid numbers. Have you run update on it? Have you set the backfill days amount?$n";
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return;
}
echo "Group ".$data["group"].": server has ".$data['first']." - ".$data['last'].", or ~";
echo((int) (($this->postdate($nntp,$data['last'],FALSE) - $this->postdate($nntp,$data['first'],FALSE))/86400));
echo " days.".$n."Local first = ".$groupArr['first_record']." (";
echo((int) ((date('U') - $this->postdate($nntp,$groupArr['first_record'],FALSE))/86400));
echo " days). Backfill target of ".$groupArr['backfill_target']."days is post $targetpost.$n";
if($targetpost >= $groupArr['first_record']) //if our estimate comes back with stuff we already have, finish
{
echo "Nothing to do, we already have the target post.$n $n";
$this->db->commit(); //Not an error so commit and re-enable autocommitting
$this->db->enableAutoCommit();
return "";
}
//get first and last part numbers from newsgroup
if($targetpost < $data['first'])
{
echo "WARNING: Backfill came back as before server's first. Setting targetpost to server first.$n";
echo "Skipping Group $n";
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return "";
}
//calculate total number of parts
$total = $groupArr['first_record'] - $targetpost;
$done = false;
//set first and last, moving the window by maxxMssgs
$last = $groupArr['first_record'] - 1;
$first = $last - $binaries->messagebuffer + 1; //set initial "chunk"
if($targetpost > $first) //just in case this is the last chunk we needed
$first = $targetpost;
while($done === false)
{
$binaries->startLoop = microtime(true);
echo "Getting ".($last-$first+1)." parts (".($first-$targetpost)." in queue)".$n;
flush();
$success = $binaries->scan($nntp, $groupArr, $first, $last, 'backfill');
if (!$success)
{
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return "";
}
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record = %s, last_updated = now() WHERE ID = %d", $this->db->escapeString($first), $groupArr['ID']));
$this->db->commit(); //At this point we are ready to commit a set up updates to the db.
if($first==$targetpost)
$done = true;
else
{ //Keep going: set new last, new first, check for last chunk.
$last = $first - 1;
$first = $last - $binaries->messagebuffer + 1;
if($targetpost > $first)
$first = $targetpost;
}
}
$first_record_postdate = $this->postdate($nntp,$first,false);
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record_postdate = FROM_UNIXTIME(".$first_record_postdate."), last_updated = now() WHERE ID = %d", $groupArr['ID'])); //Set group's first postdate
$this->db->commit(); //Commit and re-enable auto committing we are done with this group.
$this->db->enableAutoCommit();
$timeGroup = number_format(microtime(true) - $this->startGroup, 2);
echo "Group processed in $timeGroup seconds $n";
}
/**
* Returns single timestamp from a local article number.
*/
function postdate($nntp,$post,$debug=true)
{
$n = $this->n;
$attempts=0;
do
{
$msgs = $nntp->getOverview($post."-".$post,true,false);
if(PEAR::isError($msgs))
{
echo "Error {$msgs->code}: {$msgs->message}$n";
echo "Returning from postdate$n";
return "";
}
if(!isset($msgs[0]['Date']) || $msgs[0]['Date']=="" || is_null($msgs[0]['Date']))
{
$success=false;
} else {
$date = $msgs[0]['Date'];
$success=true;
}
if($debug && $attempts > 0) echo "retried $attempts time(s)".$n;
$attempts++;
} while($attempts <= 3 && $success == false);
if (!$success) { return ""; }
if($debug) echo "DEBUG: postdate for post: $post came back $date (";
$date = strtotime($date);
if($debug) echo "$date seconds unixtime or ".$this->daysOld($date)." days)".$n;
return $date;
}
/**
* Calculates the post number for a given number of days back in a group.
*/
function daytopost($nntp, $group, $days, $debug=true)
{
$n = $this->n;
$pddebug = false; //DEBUG every postdate call?!?!
if ($debug) echo "INFO: daytopost finding post for $group $days days back.".$n;
$data = $nntp->selectGroup($group);
if(PEAR::isError($data))
{
echo "Error {$data->code}: {$data->message}$n";
echo "Returning from daytopost$n";
return "";
}
$goaldate = date('U')-(86400*$days); //goaltimestamp
$totalnumberofarticles = $data['last'] - $data['first'];
$upperbound = $data['last'];
$lowerbound = $data['first'];
if ($debug) echo "Total Articles: $totalnumberofarticles $n Upper: $upperbound $n Lower: $lowerbound $n Goal: ".date("r", $goaldate)." ($goaldate) $n";
if ($data['last']==PHP_INT_MAX) { echo "ERROR: Group data is coming back as php's max value. You should not see this since we use a patched Net_NNTP that fixes this bug.$n"; die(); }
$firstDate = $this->postdate($nntp, $data['first'], $pddebug);
$lastDate = $this->postdate($nntp, $data['last'], $pddebug);
if ($goaldate < $firstDate)
{
echo "WARNING: Backfill target of $days day(s) is older than the first article stored on your news server.$n";
echo "Starting from the first available article (".date("r", $firstDate)." or ".$this->daysOld($firstDate)." days).$n";
return $data['first'];
}
elseif ($goaldate > $lastDate)
{
echo "ERROR: Backfill target of $days day(s) is newer than the last article stored on your news server.$n";
echo "To backfill this group you need to set Backfill Days to at least ".ceil($this->daysOld($lastDate)+1)." days (".date("r", $lastDate-86400).").$n";
return "";
}
if ($debug) echo "DEBUG: Searching for postdate $n Goaldate: $goaldate (".date("r", $goaldate).") $n Firstdate: $firstDate (".((is_int($firstDate))?date("r", $firstDate):'n/a').") $n Lastdate: $lastDate (".date("r", $lastDate).") $n";
$interval = floor(($upperbound - $lowerbound) * 0.5);
$dateofnextone = "";
$templowered = "";
if ($debug) echo "Start: ".$data['first']." $n End: ".$data['last']." $n Interval: $interval $n";
$dateofnextone = $lastDate;
while($this->daysOld($dateofnextone) < $days) //match on days not timestamp to speed things up
{
$nskip = 1;
while(($tmpDate = $this->postdate($nntp,($upperbound-$interval),$pddebug))>$goaldate)
{
$upperbound = $upperbound - $interval - ($nskip - 1);
if($debug) echo "New upperbound ($upperbound) is ".$this->daysOld($tmpDate)." days old. $n";
$nskip = $nskip * 2;
}
if(!$templowered)
{
$interval = ceil(($interval/2));
if($debug) echo "Set interval to $interval articles. $n";
}
$dateofnextone = $this->postdate($nntp,($upperbound-1),$pddebug);
$skip = 1;
while(!$dateofnextone)
{
$upperbound = $upperbound - $skip;
$skip = $skip * 2;
if($debug) echo "Getting next article date... $upperbound\n";
$dateofnextone = $this->postdate($nntp,($upperbound-1),$pddebug);
}
}
echo "Determined to be article $upperbound which is ".$this->daysOld($dateofnextone)." days old (".date("r", $dateofnextone).") $n";
return $upperbound;
}
/**
* Calculate the number of days from a timestamp.
*/
private function daysOld($timestamp)
{
return round((time()-$timestamp)/86400, 1);
}
/**
* Calculate the number of days from a date.
*/
private function dateToDays($backfillDate)
{
return floor(-($backfillDate - time())/(60*60*24));
}
}
-742
View File
@@ -1,742 +0,0 @@
<?php
$newzpath = getenv('NEWZPATH');
require_once("$newzpath/www/config.php");
require_once("framework/db.php");
require_once("nntp.php");
require_once("groups.php");
require_once("site.php");
require_once("backfill.php");
/**
* This class manages the downloading of binaries and parts from usenet, and the
* managing of data in the binaries and parts tables.
*/
class Binaries
{
const BLACKLIST_FIELD_SUBJECT = 1;
const BLACKLIST_FIELD_FROM = 2;
const BLACKLIST_FIELD_MESSAGEID = 3;
/**
* Default constructor
*/
function Binaries($db = null)
{
$this->n = "\n";
$s = new Sites();
$site = $s->get();
$this->compressedHeaders = ($site->compressedheaders == "1") ? true : false;
$this->messagebuffer = (!empty($site->maxmssgs)) ? $site->maxmssgs : 20000;
$this->NewGroupScanByDays = ($site->newgroupscanmethod == "1") ? true : false;
$this->NewGroupMsgsToScan = (!empty($site->newgroupmsgstoscan)) ? $site->newgroupmsgstoscan : 50000;
$this->NewGroupDaysToScan = (!empty($site->newgroupdaystoscan)) ? $site->newgroupdaystoscan : 3;
$this->blackList = array(); //cache of our black/white list
$this->message = array();
if($db == null)
{
$this->db = new DB();
}
else
{
$this->db = $db;
}
}
/*
* Allows you to set the db that the current object should use
*/
public function setDB($db)
{
$this->db = $db;
}
/**
* Process headers and store in database for all active groups.
*/
function updateAllGroups()
{
$n = $this->n;
$groups = new Groups;
$res = $groups->getActive();
$s = new Sites();
echo $s->getLicense();
if ($res)
{
shuffle($res);
$alltime = microtime(true);
echo 'Updating: '.sizeof($res).' groups - Using compression? '.(($this->compressedHeaders)?'Yes':'No').$n;
$nntp = new Nntp();
$nntp->doConnect();
$pos = 0;
foreach($res as $groupArr)
{
$pos++;
echo 'Group '.$pos.' of '.sizeof($res).$n;
$this->message = array();
$this->updateGroup($nntp, $groupArr);
}
$nntp->doQuit();
echo 'Updating completed in '.number_format(microtime(true) - $alltime, 2).' seconds'.$n;
}
else
{
echo "No groups specified. Ensure groups are added to newznab's database and activated before updating.$n";
}
}
/**
* Process headers and store in database for a group.
*/
function updateGroup($nntp=null, $groupArr)
{
$this->db->disableAutoCommit();
$blnDoDisconnect = false;
if ($nntp == null)
{
$nntp = new Nntp();
$nntp->doConnect();
$this->message = array();
$blnDoDisconnect = true;
}
$backfill = new Backfill();
$n = $this->n;
$this->startGroup = microtime(true);
echo 'Processing '.$groupArr['name'].$n;
// Connect to server
$data = $nntp->selectGroup($groupArr['name']);
if (PEAR::isError($data))
{
echo "Could not select group (bad name?): {$groupArr['name']}$n";
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
//Attempt to repair any missing parts before grabbing new ones
$this->partRepair($nntp, $groupArr);
//Get first and last part numbers from newsgroup
$last = $grouplast = $data['last'];
// For new newsgroups - determine here how far you want to go back.
if ($groupArr['last_record'] == 0)
{
if ($this->NewGroupScanByDays)
{
$first = $backfill->daytopost($nntp, $groupArr['name'], $this->NewGroupDaysToScan, true);
if ($first == '')
{
echo "Skipping group: {$groupArr['name']}$n";
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
}
else
{
if ($data['first'] > ($data['last'] - $this->NewGroupMsgsToScan))
$first = $data['first'];
else
$first = $data['last'] - $this->NewGroupMsgsToScan;
}
$first_record_postdate = $backfill->postdate($nntp, $first, false);
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record = %s, first_record_postdate = FROM_UNIXTIME(".$first_record_postdate.") WHERE ID = %d", $this->db->escapeString($first), $groupArr['ID']));
}
else
{
if ($data['last'] < $groupArr['last_record'])
{
echo "Warning: Server's last num {$data['last']} is lower than the local last num {$groupArr['last_record']}".$n;
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
$first = $groupArr['last_record'] + 1;
}
// Generate postdates for first and last records, for those that upgraded
if ((is_null($groupArr['first_record_postdate']) || is_null($groupArr['last_record_postdate'])) && ($groupArr['last_record'] != "0" && $groupArr['first_record'] != "0"))
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record_postdate = FROM_UNIXTIME(".$backfill->postdate($nntp,$groupArr['first_record'],false)."), last_record_postdate = FROM_UNIXTIME(".$backfill->postdate($nntp,$groupArr['last_record'],false).") WHERE ID = %d", $groupArr['ID']));
// Deactivate empty groups
if (($data['last'] - $data['first']) <= 5)
$this->db->mysqliQuery(sprintf("UPDATE groups SET active = %s, last_updated = now() WHERE ID = %d", $this->db->escapeString('0'), $groupArr['ID']));
// Calculate total number of parts
$total = $grouplast - $first + 1;
// If total is bigger than 0 it means we have new parts in the newsgroup
if($total > 0)
{
echo "Group ".$data["group"]." has ".number_format($total)." new parts.".$n;
echo "First: ".$data['first']." Last: ".$data['last']." Local last: ".$groupArr['last_record'].$n;
if ($groupArr['last_record'] == 0)
echo "New group starting with ".(($this->NewGroupScanByDays) ? $this->NewGroupDaysToScan." days" : $this->NewGroupMsgsToScan." messages")." worth.".$n;
$done = false;
// Get all the parts (in portions of $this->messagebuffer to not use too much memory)
while ($done === false)
{
$this->startLoop = microtime(true);
if ($total > $this->messagebuffer)
{
if ($first + $this->messagebuffer > $grouplast)
$last = $grouplast;
else
$last = $first + $this->messagebuffer;
}
echo "Getting ".number_format($last-$first+1)." parts (".$first." to ".$last.") - ".number_format($grouplast - $last)." in queue".$n;
flush();
//get headers from newsgroup
$lastId = $this->scan($nntp, $groupArr, $first, $last);
if ($lastId === false)
{
//scan failed - skip group
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
$this->db->mysqliQuery(sprintf("UPDATE groups SET last_record = %s, last_updated = now() WHERE ID = %d", $this->db->escapeString($lastId), $groupArr['ID']));
$this->db->commit(); //At this point we are ready to commit a whole group to the db.
if ($last == $grouplast)
$done = true;
else
{
$last = $lastId;
$first = $last + 1;
}
}
$last_record_postdate = $backfill->postdate($nntp,$last,false);
$this->db->mysqliQuery(sprintf("UPDATE groups SET last_record_postdate = FROM_UNIXTIME(".$last_record_postdate."), last_updated = now() WHERE ID = %d", $groupArr['ID'])); //Set group's last postdate
$timeGroup = number_format(microtime(true) - $this->startGroup, 2);
echo "Group processed in $timeGroup seconds $n $n";
}
else
{
echo "No new records for ".$data["group"]." (first $first last $last total $total) grouplast ".$groupArr['last_record'].$n.$n;
}
if ($blnDoDisconnect)
{
$nntp->doQuit();
}
/*
* Got through the updating of this group successfully
* so commit and re-enable the auto committing.
*/
$this->db->commit(); //For anything not yet committed.
$this->db->enableAutoCommit();
}
/**
* Download a range of usenet messages. Store binaries with subjects matching a
* specific pattern in the database.
*/
function scan($nntp, $groupArr, $first, $last, $type='update')
{
$n = $this->n;
$this->startHeaders = microtime(true);
if ($this->compressedHeaders)
$msgs = $nntp->getXOverview($first."-".$last, true, false);
else
$msgs = $nntp->getOverview($first."-".$last, true, false);
if (PEAR::isError($msgs) && $msgs->code == 400)
{
echo "NNTP connection timed out. Reconnecting...$n";
$nntp->doConnect();
$nntp->selectGroup($groupArr['name']);
if ($this->compressedHeaders)
$msgs = $nntp->getXOverview($first."-".$last, true, false);
else
$msgs = $nntp->getOverview($first."-".$last, true, false);
}
$rangerequested = range($first, $last);
$msgsreceived = array();
$msgsblacklisted = array();
$msgsignored = array();
$msgsinserted = array();
$msgsnotinserted = array();
$timeHeaders = number_format(microtime(true) - $this->startHeaders, 2);
if(PEAR::isError($msgs))
{
echo "Error {$msgs->code}: {$msgs->message}$n";
echo "Skipping group$n";
return false;
}
$this->startUpdate = microtime(true);
if (is_array($msgs))
{
//loop headers, figure out parts
foreach($msgs AS $msg)
{
if (!isset($msg['Number']))
continue;
$msgsreceived[] = $msg['Number'];
$msgPart = $msgTotalParts = 0;
$pattern = '|\((\d+)[\/](\d+)\)|i';
preg_match_all($pattern, $msg['Subject'], $matches, PREG_PATTERN_ORDER);
$matchcnt = sizeof($matches[0]);
for ($i=0; $i<$matchcnt; $i++)
{
$msgPart = $matches[1][$i];
$msgTotalParts = $matches[2][$i];
}
if (!isset($msg['Subject']) || $matchcnt == 0) // not a binary post most likely.. continue
{
$msgsignored[] = $msg['Number'];
continue;
}
//Filter binaries based on black/white list
if ($this->isBlackListed($msg, $groupArr['name']))
{
$msgsblacklisted[] = $msg['Number'];
continue;
}
if((int)$msgPart > 0 && (int)$msgTotalParts > 0)
{
$subject = utf8_encode(trim(preg_replace('|\('.$msgPart.'[\/]'.$msgTotalParts.'\)|i', '', $msg['Subject'])));
if(!isset($this->message[$subject]))
{
$this->message[$subject] = $msg;
$this->message[$subject]['MaxParts'] = (int) $msgTotalParts;
$this->message[$subject]['Date'] = strtotime($this->message[$subject]['Date']);
}
if((int)$msgPart > 0)
{
$this->message[$subject]['Parts'][(int)$msgPart] = array('Message-ID' => substr($msg['Message-ID'],1,-1), 'number' => $msg['Number'], 'part' => (int)$msgPart, 'size' => $msg['Bytes']);
}
}
}
unset($msg);
unset($msgs);
$count = 0;
$updatecount = 0;
$partcount = 0;
$maxnum = $last;
$rangenotreceived = array_diff($rangerequested, $msgsreceived);
if ($type != 'partrepair')
echo "Received ".sizeof($msgsreceived)." articles of ".($last-$first+1)." requested, ".sizeof($msgsblacklisted)." blacklisted, ".sizeof($msgsignored)." not binaries $n";
if ($type == 'update' && sizeof($msgsreceived) == 0)
{
echo "Error: Server did not return any articles.$n";
echo "Skipping group$n";
return false;
}
if (sizeof($rangenotreceived) > 0) {
switch($type)
{
case 'backfill':
//don't add missing articles
break;
case 'partrepair':
case 'update':
default:
$this->addMissingParts($rangenotreceived, $groupArr['ID']);
break;
}
echo "Server did not return ".count($rangenotreceived)." article(s).$n";
}
if(isset($this->message) && count($this->message))
{
$maxnum = $first;
//insert binaries and parts into database. when binary already exists; only insert new parts
foreach($this->message AS $subject => $data)
{
if(isset($data['Parts']) && count($data['Parts']) > 0 && $subject != '')
{
$binaryHash = md5($subject.$data['From'].$groupArr['ID']);
$res = $this->db->mysqliQueryOneRow(sprintf("SELECT ID FROM binaries WHERE binaryhash = %s", $this->db->escapeString($binaryHash)));
if(!$res)
{
$sql = sprintf("INSERT INTO binaries (name, fromname, date, xref, totalparts, groupID, binaryhash, dateadded) VALUES (%s, %s, FROM_UNIXTIME(%s), %s, %s, %d, %s, now())", $this->db->escapeString($subject), $this->db->escapeString($data['From']), $this->db->escapeString($data['Date']), $this->db->escapeString($data['Xref']), $this->db->escapeString($data['MaxParts']), $groupArr['ID'], $this->db->escapeString($binaryHash));
$binaryID = $this->db->mysqliQueryInsert($sql);
$count++;
if ($count%500==0) echo "$count bin adds...";
}
else
{
$binaryID = $res["ID"];
$updatecount++;
if ($updatecount%500==0) echo "$updatecount bin updates...";
}
foreach($data['Parts'] AS $partdata)
{
$maxnum = ($partdata['number'] > $maxnum) ? $partdata['number'] : $maxnum;
$partcount++;
$pidata = $this->db->mysqliQueryInsert(sprintf("INSERT INTO parts (binaryID, messageID, number, partnumber, size, dateadded) VALUES (%d, %s, %s, %s, %s, now())", $binaryID, $this->db->escapeString($partdata['Message-ID']), $this->db->escapeString($partdata['number']), $this->db->escapeString(round($partdata['part'])), $this->db->escapeString($partdata['size'])), false);
if (!$pidata) {
$msgsnotinserted[] = $partdata['number'];
} else {
$msgsinserted[] = $partdata['number'];
}
}
}
}
//TODO: determine whether to add to missing articles if insert failed
if (sizeof($msgsnotinserted) > 0)
{
echo 'WARNING: ' . count($msgsnotinserted) . ' Parts failed to insert'.$n;
$this->addMissingParts($msgsnotinserted, $groupArr['ID']);
}
if (($count >= 500) || ($updatecount >= 500)) { echo $n; } //line break for bin adds output
}
$timeUpdate = number_format(microtime(true) - $this->startUpdate, 2);
$timeLoop = number_format(microtime(true)-$this->startLoop, 2);
if ($type != 'partrepair')
{
echo number_format($count).' new, '.number_format($updatecount).' updated, '.number_format($partcount).' parts.';
echo " $timeHeaders headers, $timeUpdate update, $timeLoop range.$n";
}
unset($this->message);
unset($data);
return $maxnum;
}
else
{
echo "Error: Can't get parts from server (msgs not array) $n";
echo "Skipping group$n";
return false;
}
}
/**
* Go through all rows in partrepair table and see if theyve arrived on usenet yet.
*/
private function partRepair($nntp, $groupArr)
{
$n = $this->n;
//get all parts in partrepair table
$missingParts = $this->db->mysqliQuery(sprintf("SELECT * FROM partrepair WHERE groupID = %d AND attempts < 5 ORDER BY numberID ASC LIMIT 30000", $groupArr['ID']));
$partsRepaired = $partsFailed = 0;
if (sizeof($missingParts) > 0)
{
echo 'Attempting to repair '.sizeof($missingParts).' parts...';
//loop through each part to group into ranges
$ranges = array();
$lastnum = $lastpart = 0;
foreach($missingParts as $part)
{
if (($lastnum+1) == $part['numberID']) {
$ranges[$lastpart] = $part['numberID'];
} else {
$lastpart = $part['numberID'];
$ranges[$lastpart] = $part['numberID'];
}
$lastnum = $part['numberID'];
}
//download missing parts in ranges
foreach($ranges as $partfrom=>$partto)
{
$this->startLoop = microtime(true);
echo ".";
//get article from newsgroup
$this->scan($nntp, $groupArr, $partfrom, $partto, 'partrepair');
//check if the articles were added
$articles = implode(',', range($partfrom, $partto));
$sql = sprintf("SELECT pr.ID, pr.numberID, p.number from partrepair pr LEFT JOIN parts p ON p.number = pr.numberID WHERE pr.groupID=%d AND pr.numberID IN (%s) ORDER BY pr.numberID ASC", $groupArr['ID'], $articles);
$result = $this->db->mysqliQueryDirect($sql);
while ($r = mysqli_fetch_assoc($result))
{
if (isset($r['number']) && $r['number'] == $r['numberID'])
{
$partsRepaired++;
//article was added, delete from partrepair
$this->db->mysqliQuery(sprintf("DELETE FROM partrepair WHERE ID=%d", $r['ID']));
}
else
{
$partsFailed++;
//article was not added, increment attempts
$this->db->mysqliQuery(sprintf("UPDATE partrepair SET attempts=attempts+1 WHERE ID=%d", $r['ID']));
}
}
}
echo $n.$partsRepaired.' parts repaired.'.$n;
}
//remove articles that we cant fetch after 5 attempts
$this->db->mysqliQuery(sprintf("DELETE FROM partrepair WHERE attempts >= 5 AND groupID = %d", $groupArr['ID']));
}
/**
* Insert a missing part to the database.
*/
private function addMissingParts($numbers, $groupID)
{
$added = false;
$insertStr = "INSERT INTO partrepair (numberID, groupID) VALUES ";
foreach($numbers as $number)
{
if ($number > 0)
{
$added = true;
$insertStr .= sprintf("(%u, %d), ", $number, $groupID);
}
}
if ($added)
{
$insertStr = substr($insertStr, 0, -2);
$insertStr .= " ON DUPLICATE KEY UPDATE attempts=attempts+1";
return $this->db->mysqliQueryInsert($insertStr, false);
}
return -1;
}
/**
* Return internally cached list of binary blacklist patterns.
*/
public function retrieveBlackList()
{
if (is_array($this->blackList) && !empty($this->blackList)) { return $this->blackList; }
$blackList = $this->getBlacklist(true);
$this->blackList = $blackList;
return $blackList;
}
/**
* Test if a message subject is blacklisted.
*/
public function isBlackListed($msg, $groupName)
{
$blackList = $this->retrieveBlackList();
$field = array();
if (isset($msg["Subject"]))
$field[Binaries::BLACKLIST_FIELD_SUBJECT] = $msg["Subject"];
if (isset($msg["From"]))
$field[Binaries::BLACKLIST_FIELD_FROM] = $msg["From"];
if (isset($msg["Message-ID"]))
$field[Binaries::BLACKLIST_FIELD_MESSAGEID] = $msg["Message-ID"];
foreach ($blackList as $blist)
{
if (preg_match('/^'.$blist['groupname'].'$/i', $groupName))
{
//blacklist
if ($blist['optype'] == 1)
{
if (preg_match('/'.$blist['regex'].'/i', $field[$blist['msgcol']])) {
return true;
}
}
else if ($blist['optype'] == 2)
{
if (!preg_match('/'.$blist['regex'].'/i', $field[$blist['msgcol']])) {
return true;
}
}
}
}
return false;
}
/**
* Rawsearch. Perform a simple like match on binary subjects matching a pattern.
*/
public function search($search, $limit=1000, $excludedcats=array())
{
//
// if the query starts with a ^ it indicates the search is looking for items which start with the term
// still do the like match, but mandate that all items returned must start with the provided word
//
$words = explode(" ", $search);
$searchsql = "";
$intwordcount = 0;
if (count($words) > 0)
{
foreach ($words as $word)
{
//
// see if the first word had a caret, which indicates search must start with term
//
if ($intwordcount == 0 && (strpos($word, "^") === 0))
$searchsql.= sprintf(" and b.name like %s", $this->db->escapeString(substr($word, 1)."%"));
else
$searchsql.= sprintf(" and b.name like %s", $this->db->escapeString("%".$word."%"));
$intwordcount++;
}
}
$exccatlist = "";
if (count($excludedcats) > 0)
$exccatlist = " and b.categoryID not in (".implode(",", $excludedcats).") ";
$res = $this->db->mysqliQuery(sprintf("
SELECT b.*,
g.name AS group_name,
r.guid,
(SELECT COUNT(ID) FROM parts p where p.binaryID = b.ID) as 'binnum'
FROM binaries b
INNER JOIN groups g ON g.ID = b.groupID
LEFT OUTER JOIN releases r ON r.ID = b.releaseID
WHERE 1=1 %s %s order by DATE DESC LIMIT %d ",
$searchsql, $exccatlist, $limit));
return $res;
}
/**
* Get all binaries for a release.
*/
public function getForReleaseId($id)
{
return $this->db->mysqliQuery(sprintf("select binaries.* from binaries where releaseID = %d order by relpart", $id));
}
/**
* Get a binary row.
*/
public function getById($id)
{
return $this->db->mysqliQueryOneRow(sprintf("select binaries.*, groups.name as groupname from binaries left outer join groups on binaries.groupID = groups.ID where binaries.ID = %d ", $id));
}
/**
* Get list of blacklists from database.
*/
public function getBlacklist($activeonly=true)
{
$where = "";
if ($activeonly)
$where = " where binaryblacklist.status = 1 ";
return $this->db->mysqliQuery("SELECT binaryblacklist.ID, binaryblacklist.optype, binaryblacklist.status, binaryblacklist.description, binaryblacklist.groupname AS groupname, binaryblacklist.regex,
groups.ID AS groupID, binaryblacklist.msgcol FROM binaryblacklist
left outer JOIN groups ON groups.name = binaryblacklist.groupname
".$where."
ORDER BY coalesce(groupname,'zzz')");
}
/**
* Get a blacklist row from database.
*/
public function getBlacklistByID($id)
{
return $this->db->mysqliQueryOneRow(sprintf("select * from binaryblacklist where ID = %d ", $id));
}
/**
* Delete a blacklist row from database.
*/
public function deleteBlacklist($id)
{
return $this->db->mysqliQuery(sprintf("delete from binaryblacklist where ID = %d", $id));
}
/**
* Update a blacklist row.
*/
public function updateBlacklist($regex)
{
$groupname = $regex["groupname"];
if ($groupname == "")
$groupname = "null";
else
{
$groupname = preg_replace("/a\.b\./i", "alt.binaries.", $groupname);
$groupname = sprintf("%s", $this->db->escapeString($groupname));
}
$this->db->mysqliQuery(sprintf("update binaryblacklist set groupname=%s, regex=%s, status=%d, description=%s, optype=%d, msgcol=%d where ID = %d ", $groupname, $this->db->escapeString($regex["regex"]), $regex["status"], $this->db->escapeString($regex["description"]), $regex["optype"], $regex["msgcol"], $regex["id"]));
}
/**
* Add a new blacklist row.
*/
public function addBlacklist($regex)
{
$groupname = $regex["groupname"];
if ($groupname == "")
$groupname = "null";
else
{
$groupname = preg_replace("/a\.b\./i", "alt.binaries.", $groupname);
$groupname = sprintf("%s", $this->db->escapeString($groupname));
}
return $this->db->mysqliQueryInsert(sprintf("insert into binaryblacklist (groupname, regex, status, description, optype, msgcol) values (%s, %s, %d, %s, %d, %d) ",
$groupname, $this->db->escapeString($regex["regex"]), $regex["status"], $this->db->escapeString($regex["description"]), $regex["optype"], $regex["msgcol"]));
}
/**
* Add a new binary row and its associated parts.
*/
public function delete($id)
{
$this->db->mysqliQuery(sprintf("delete from parts where binaryID = %d", $id));
$this->db->mysqliQuery(sprintf("delete from binaries where ID = %d", $id));
}
}
-175
View File
@@ -1,175 +0,0 @@
<?php
class DB
{
private static $initialized = false;
function DB()
{
if (DB::$initialized === false)
{
// initialize db connection
mysql_pconnect(DB_HOST, DB_USER, DB_PASSWORD)
or die("fatal error: could not connect to database! Check your config.");
mysql_select_db(DB_NAME)
or die("fatal error: could not select database! Check your config.");
mysql_set_charset('utf8');
DB::$initialized = true;
}
$this->mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
}
public function escapeString($str)
{
return "'".mysql_real_escape_string($str)."'";
}
public function makeLookupTable($rows, $keycol)
{
$arr = array();
foreach($rows as $row)
$arr[$row[$keycol]] = $row;
return $arr;
}
public function queryInsert($query, $returnlastid=true)
{
$result = mysql_query($query);
return ($returnlastid) ? mysql_insert_id() : $result;
}
public function queryOneRow($query)
{
$rows = $this->query($query);
if (!$rows)
return false;
if ($rows)
return $rows[0];
else
return $rows;
}
public function query($query)
{
$result = mysql_query($query);
if ($result === false || $result === true)
return array();
$rows = array();
while ($row = mysql_fetch_assoc($result))
$rows[] = $row;
mysql_free_result($result);
return $rows;
}
public function queryDirect($query)
{
$result = mysql_query($query);
return $result;
}
public function optimise()
{
$ret = array();
$alltables = $this->query("SHOW TABLES");
foreach ($alltables as $tablename)
{
$ret[] = $tablename['Tables_in_'.DB_NAME];
$this->queryDirect("REPAIR TABLE `".$tablename['Tables_in_'.DB_NAME]."`");
$this->queryDirect("OPTIMIZE TABLE `".$tablename['Tables_in_'.DB_NAME]."`");
}
return $ret;
}
/* Mysqli Functions */
public function multiQueryTransaction($query)
{
$this->mysqli->autocommit(FALSE);
$this->mysqli->multi_query($query);
while($this->mysqli->next_result()) {
$result = $this->mysqli->use_result();
if($result instanceof mysqli_result)
$result->free();
}
$this->mysqli->commit();
}
public function disableAutoCommit()
{
$this->mysqli->autocommit(FALSE);
}
public function mysqliQuery($sql)
{
$result = $this->mysqli->query($sql);
if ($result === false || $result === true)
return array();
$rows = array();
while ($row = mysqli_fetch_assoc($result))
$rows[] = $row;
mysqli_free_result($result);
return $rows;
}
public function mysqliQueryOneRow($query)
{
$rows = $this->mysqliQuery($query);
if (!$rows)
return false;
if ($rows)
return $rows[0];
else
return $rows;
}
public function mysqliQueryInsert($query, $returnlastid=true)
{
$result = $this->mysqli->query($query);
return ($returnlastid) ? $this->mysqli->insert_id : $result;
}
public function commit()
{
$this->mysqli->commit();
}
public function rollback()
{
$this->mysqli->rollback();
}
public function mysqliQueryDirect($query)
{
return $this->mysqli->query($query);
}
public function enableAutoCommit()
{
$this->mysqli->autocommit(TRUE);
}
}
?>
-27
View File
@@ -1,27 +0,0 @@
<?php
require_once("framework/db.php");
/**
* This class handles data access for groups.
*/
class Groups
{
/**
* Get all active group rows.
*/
public function getActive()
{
$db = new DB();
return $db->query("SELECT * FROM groups WHERE active = 1 ORDER BY name");
}
/**
* Get a group row by name.
*/
public function getByName($grp)
{
$db = new DB();
return $db->queryOneRow(sprintf("select * from groups where name = '%s' ", $grp));
}
}
-300
View File
@@ -1,300 +0,0 @@
<?php
require_once("config.php");
require_once(dirname(__FILE__)."/../framework/db.php");
require_once(dirname(__FILE__)."/../groups.php");
require_once(dirname(__FILE__)."/../site.php");
require_once(dirname(__FILE__)."/../nntp.php");
require_once("binaries.php");
/**
* Retrieves messages from usenet based on provided backfill-to date.
*/
class Backfill
{
/**
* Default constructor.
*/
function Backfill($db = null)
{
if($db == null)
{
$this->db = new DB();
}
else
{
$this->db = $db;
}
$this->n = "\n";
}
/**
* Update all active groups categories and descriptions.
*/
function backfillAllGroups($groupName='', $backfillDate=null)
{
$n = $this->n;
$groups = new Groups;
$res = false;
if ($groupName != '')
{
$grp = $groups->getByName($groupName);
if ($grp)
$res = array($grp);
}
else
{
$res = $groups->getActive();
}
if ($res)
{
$nntp = new Nntp();
$nntp->doConnect();
foreach($res as $groupArr)
{
$this->backfillGroup($nntp, $groupArr, $backfillDate);
}
$nntp->doQuit();
}
else
{
echo "No groups specified. Ensure groups are added to newznab's database for updating.$n";
}
}
/**
* Update a group back to a specified date.
*/
function backfillGroup($nntp, $groupArr, $backfillDate=null)
{
$this->db->disableAutoCommit(); //Turn off auto committing
$binaries = new Binaries($this->db);
$n = $this->n;
$this->startGroup = microtime(true);
echo 'Processing '.$groupArr['name'].$n;
$data = $nntp->selectGroup($groupArr['name']);
if(PEAR::isError($data))
{
echo "Could not select group (bad name?): {$groupArr['name']}$n";
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return;
}
if ($backfillDate)
$targetpost = $this->daytopost($nntp,$groupArr['name'],$this->dateToDays($backfillDate),TRUE); // get targetpost based on date
else
$targetpost = $this->daytopost($nntp,$groupArr['name'],$groupArr['backfill_target'],TRUE); //get targetpost based on days target
if($groupArr['first_record'] == 0 || $groupArr['backfill_target'] == 0)
{
echo "Group ".$groupArr['name']." has invalid numbers. Have you run update on it? Have you set the backfill days amount?$n";
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return;
}
echo "Group ".$data["group"].": server has ".$data['first']." - ".$data['last'].", or ~";
echo((int) (($this->postdate($nntp,$data['last'],FALSE) - $this->postdate($nntp,$data['first'],FALSE))/86400));
echo " days.".$n."Local first = ".$groupArr['first_record']." (";
echo((int) ((date('U') - $this->postdate($nntp,$groupArr['first_record'],FALSE))/86400));
echo " days). Backfill target of ".$groupArr['backfill_target']."days is post $targetpost.$n";
if($targetpost >= $groupArr['first_record']) //if our estimate comes back with stuff we already have, finish
{
echo "Nothing to do, we already have the target post.$n $n";
$this->db->commit(); //Not an error so commit and re-enable autocommitting
$this->db->enableAutoCommit();
return "";
}
//get first and last part numbers from newsgroup
if($targetpost < $data['first'])
{
echo "WARNING: Backfill came back as before server's first. Setting targetpost to server first.$n";
echo "Skipping Group $n";
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return "";
}
//calculate total number of parts
$total = $groupArr['first_record'] - $targetpost;
$done = false;
//set first and last, moving the window by maxxMssgs
$last = $groupArr['first_record'] - 1;
$first = $last - $binaries->messagebuffer + 1; //set initial "chunk"
if($targetpost > $first) //just in case this is the last chunk we needed
$first = $targetpost;
while($done === false)
{
$binaries->startLoop = microtime(true);
echo "Getting ".($last-$first+1)." parts (".($first-$targetpost)." in queue)".$n;
flush();
$success = $binaries->scan($nntp, $groupArr, $first, $last, 'backfill');
if (!$success)
{
$this->db->rollback(); //Rollback and re-enable autocommitting
$this->db->enableAutoCommit();
return "";
}
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record = %s, last_updated = now() WHERE ID = %d", $this->db->escapeString($first), $groupArr['ID']));
$this->db->commit(); //At this point we are ready to commit a set up updates to the db.
if($first==$targetpost)
$done = true;
else
{ //Keep going: set new last, new first, check for last chunk.
$last = $first - 1;
$first = $last - $binaries->messagebuffer + 1;
if($targetpost > $first)
$first = $targetpost;
}
}
$first_record_postdate = $this->postdate($nntp,$first,false);
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record_postdate = FROM_UNIXTIME(".$first_record_postdate."), last_updated = now() WHERE ID = %d", $groupArr['ID'])); //Set group's first postdate
$this->db->commit(); //Commit and re-enable auto committing we are done with this group.
$this->db->enableAutoCommit();
$timeGroup = number_format(microtime(true) - $this->startGroup, 2);
echo "Group processed in $timeGroup seconds $n";
}
/**
* Returns single timestamp from a local article number.
*/
function postdate($nntp,$post,$debug=true)
{
$n = $this->n;
$attempts=0;
do
{
$msgs = $nntp->getOverview($post."-".$post,true,false);
if(PEAR::isError($msgs))
{
echo "Error {$msgs->code}: {$msgs->message}$n";
echo "Returning from postdate$n";
return "";
}
if(!isset($msgs[0]['Date']) || $msgs[0]['Date']=="" || is_null($msgs[0]['Date']))
{
$success=false;
} else {
$date = $msgs[0]['Date'];
$success=true;
}
if($debug && $attempts > 0) echo "retried $attempts time(s)".$n;
$attempts++;
} while($attempts <= 3 && $success == false);
if (!$success) { return ""; }
if($debug) echo "DEBUG: postdate for post: $post came back $date (";
$date = strtotime($date);
if($debug) echo "$date seconds unixtime or ".$this->daysOld($date)." days)".$n;
return $date;
}
/**
* Calculates the post number for a given number of days back in a group.
*/
function daytopost($nntp, $group, $days, $debug=true)
{
$n = $this->n;
$pddebug = false; //DEBUG every postdate call?!?!
if ($debug) echo "INFO: daytopost finding post for $group $days days back.".$n;
$data = $nntp->selectGroup($group);
if(PEAR::isError($data))
{
echo "Error {$data->code}: {$data->message}$n";
echo "Returning from daytopost$n";
return "";
}
$goaldate = date('U')-(86400*$days); //goaltimestamp
$totalnumberofarticles = $data['last'] - $data['first'];
$upperbound = $data['last'];
$lowerbound = $data['first'];
if ($debug) echo "Total Articles: $totalnumberofarticles $n Upper: $upperbound $n Lower: $lowerbound $n Goal: ".date("r", $goaldate)." ($goaldate) $n";
if ($data['last']==PHP_INT_MAX) { echo "ERROR: Group data is coming back as php's max value. You should not see this since we use a patched Net_NNTP that fixes this bug.$n"; die(); }
$firstDate = $this->postdate($nntp, $data['first'], $pddebug);
$lastDate = $this->postdate($nntp, $data['last'], $pddebug);
if ($goaldate < $firstDate)
{
echo "WARNING: Backfill target of $days day(s) is older than the first article stored on your news server.$n";
echo "Starting from the first available article (".date("r", $firstDate)." or ".$this->daysOld($firstDate)." days).$n";
return $data['first'];
}
elseif ($goaldate > $lastDate)
{
echo "ERROR: Backfill target of $days day(s) is newer than the last article stored on your news server.$n";
echo "To backfill this group you need to set Backfill Days to at least ".ceil($this->daysOld($lastDate)+1)." days (".date("r", $lastDate-86400).").$n";
return "";
}
if ($debug) echo "DEBUG: Searching for postdate $n Goaldate: $goaldate (".date("r", $goaldate).") $n Firstdate: $firstDate (".((is_int($firstDate))?date("r", $firstDate):'n/a').") $n Lastdate: $lastDate (".date("r", $lastDate).") $n";
$interval = floor(($upperbound - $lowerbound) * 0.5);
$dateofnextone = "";
$templowered = "";
if ($debug) echo "Start: ".$data['first']." $n End: ".$data['last']." $n Interval: $interval $n";
$dateofnextone = $lastDate;
while($this->daysOld($dateofnextone) < $days) //match on days not timestamp to speed things up
{
$nskip = 1;
while(($tmpDate = $this->postdate($nntp,($upperbound-$interval),$pddebug))>$goaldate)
{
$upperbound = $upperbound - $interval - ($nskip - 1);
if($debug) echo "New upperbound ($upperbound) is ".$this->daysOld($tmpDate)." days old. $n";
$nskip = $nskip * 2;
}
if(!$templowered)
{
$interval = ceil(($interval/2));
if($debug) echo "Set interval to $interval articles. $n";
}
$dateofnextone = $this->postdate($nntp,($upperbound-1),$pddebug);
$skip = 1;
while(!$dateofnextone)
{
$upperbound = $upperbound - $skip;
$skip = $skip * 2;
if($debug) echo "Getting next article date... $upperbound\n";
$dateofnextone = $this->postdate($nntp,($upperbound-1),$pddebug);
}
}
echo "Determined to be article $upperbound which is ".$this->daysOld($dateofnextone)." days old (".date("r", $dateofnextone).") $n";
return $upperbound;
}
/**
* Calculate the number of days from a timestamp.
*/
private function daysOld($timestamp)
{
return round((time()-$timestamp)/86400, 1);
}
/**
* Calculate the number of days from a date.
*/
private function dateToDays($backfillDate)
{
return floor(-($backfillDate - time())/(60*60*24));
}
}
-741
View File
@@ -1,741 +0,0 @@
<?php
require_once("config.php");
require_once(dirname(__FILE__)."/../framework/db.php");
require_once(dirname(__FILE__)."/../nntp.php");
require_once(dirname(__FILE__)."/../groups.php");
require_once(dirname(__FILE__)."/../site.php");
require_once(dirname(__FILE__)."/../backfill.php");
/**
* This class manages the downloading of binaries and parts from usenet, and the
* managing of data in the binaries and parts tables.
*/
class Binaries
{
const BLACKLIST_FIELD_SUBJECT = 1;
const BLACKLIST_FIELD_FROM = 2;
const BLACKLIST_FIELD_MESSAGEID = 3;
/**
* Default constructor
*/
function Binaries($db = null)
{
$this->n = "\n";
$s = new Sites();
$site = $s->get();
$this->compressedHeaders = ($site->compressedheaders == "1") ? true : false;
$this->messagebuffer = (!empty($site->maxmssgs)) ? $site->maxmssgs : 20000;
$this->NewGroupScanByDays = ($site->newgroupscanmethod == "1") ? true : false;
$this->NewGroupMsgsToScan = (!empty($site->newgroupmsgstoscan)) ? $site->newgroupmsgstoscan : 50000;
$this->NewGroupDaysToScan = (!empty($site->newgroupdaystoscan)) ? $site->newgroupdaystoscan : 3;
$this->blackList = array(); //cache of our black/white list
$this->message = array();
if($db == null)
{
$this->db = new DB();
}
else
{
$this->db = $db;
}
}
/*
* Allows you to set the db that the current object should use
*/
public function setDB($db)
{
$this->db = $db;
}
/**
* Process headers and store in database for all active groups.
*/
function updateAllGroups()
{
$n = $this->n;
$groups = new Groups;
$res = $groups->getActive();
$s = new Sites();
echo $s->getLicense();
if ($res)
{
shuffle($res);
$alltime = microtime(true);
echo 'Updating: '.sizeof($res).' groups - Using compression? '.(($this->compressedHeaders)?'Yes':'No').$n;
$nntp = new Nntp();
$nntp->doConnect();
$pos = 0;
foreach($res as $groupArr)
{
$pos++;
echo 'Group '.$pos.' of '.sizeof($res).$n;
$this->message = array();
$this->updateGroup($nntp, $groupArr);
}
$nntp->doQuit();
echo 'Updating completed in '.number_format(microtime(true) - $alltime, 2).' seconds'.$n;
}
else
{
echo "No groups specified. Ensure groups are added to newznab's database and activated before updating.$n";
}
}
/**
* Process headers and store in database for a group.
*/
function updateGroup($nntp=null, $groupArr)
{
$this->db->disableAutoCommit();
$blnDoDisconnect = false;
if ($nntp == null)
{
$nntp = new Nntp();
$nntp->doConnect();
$this->message = array();
$blnDoDisconnect = true;
}
$backfill = new Backfill();
$n = $this->n;
$this->startGroup = microtime(true);
echo 'Processing '.$groupArr['name'].$n;
// Connect to server
$data = $nntp->selectGroup($groupArr['name']);
if (PEAR::isError($data))
{
echo "Could not select group (bad name?): {$groupArr['name']}$n";
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
//Attempt to repair any missing parts before grabbing new ones
$this->partRepair($nntp, $groupArr);
//Get first and last part numbers from newsgroup
$last = $grouplast = $data['last'];
// For new newsgroups - determine here how far you want to go back.
if ($groupArr['last_record'] == 0)
{
if ($this->NewGroupScanByDays)
{
$first = $backfill->daytopost($nntp, $groupArr['name'], $this->NewGroupDaysToScan, true);
if ($first == '')
{
echo "Skipping group: {$groupArr['name']}$n";
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
}
else
{
if ($data['first'] > ($data['last'] - $this->NewGroupMsgsToScan))
$first = $data['first'];
else
$first = $data['last'] - $this->NewGroupMsgsToScan;
}
$first_record_postdate = $backfill->postdate($nntp, $first, false);
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record = %s, first_record_postdate = FROM_UNIXTIME(".$first_record_postdate.") WHERE ID = %d", $this->db->escapeString($first), $groupArr['ID']));
}
else
{
if ($data['last'] < $groupArr['last_record'])
{
echo "Warning: Server's last num {$data['last']} is lower than the local last num {$groupArr['last_record']}".$n;
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
$first = $groupArr['last_record'] + 1;
}
// Generate postdates for first and last records, for those that upgraded
if ((is_null($groupArr['first_record_postdate']) || is_null($groupArr['last_record_postdate'])) && ($groupArr['last_record'] != "0" && $groupArr['first_record'] != "0"))
$this->db->mysqliQuery(sprintf("UPDATE groups SET first_record_postdate = FROM_UNIXTIME(".$backfill->postdate($nntp,$groupArr['first_record'],false)."), last_record_postdate = FROM_UNIXTIME(".$backfill->postdate($nntp,$groupArr['last_record'],false).") WHERE ID = %d", $groupArr['ID']));
// Deactivate empty groups
if (($data['last'] - $data['first']) <= 5)
$this->db->mysqliQuery(sprintf("UPDATE groups SET active = %s, last_updated = now() WHERE ID = %d", $this->db->escapeString('0'), $groupArr['ID']));
// Calculate total number of parts
$total = $grouplast - $first + 1;
// If total is bigger than 0 it means we have new parts in the newsgroup
if($total > 0)
{
echo "Group ".$data["group"]." has ".number_format($total)." new parts.".$n;
echo "First: ".$data['first']." Last: ".$data['last']." Local last: ".$groupArr['last_record'].$n;
if ($groupArr['last_record'] == 0)
echo "New group starting with ".(($this->NewGroupScanByDays) ? $this->NewGroupDaysToScan." days" : $this->NewGroupMsgsToScan." messages")." worth.".$n;
$done = false;
// Get all the parts (in portions of $this->messagebuffer to not use too much memory)
while ($done === false)
{
$this->startLoop = microtime(true);
if ($total > $this->messagebuffer)
{
if ($first + $this->messagebuffer > $grouplast)
$last = $grouplast;
else
$last = $first + $this->messagebuffer;
}
echo "Getting ".number_format($last-$first+1)." parts (".$first." to ".$last.") - ".number_format($grouplast - $last)." in queue".$n;
flush();
//get headers from newsgroup
$lastId = $this->scan($nntp, $groupArr, $first, $last);
if ($lastId === false)
{
//scan failed - skip group
$this->db->rollback(); //Rollback and re-enable auto committing
$this->db->enableAutoCommit();
return;
}
$this->db->mysqliQuery(sprintf("UPDATE groups SET last_record = %s, last_updated = now() WHERE ID = %d", $this->db->escapeString($lastId), $groupArr['ID']));
$this->db->commit(); //At this point we are ready to commit a whole group to the db.
if ($last == $grouplast)
$done = true;
else
{
$last = $lastId;
$first = $last + 1;
}
}
$last_record_postdate = $backfill->postdate($nntp,$last,false);
$this->db->mysqliQuery(sprintf("UPDATE groups SET last_record_postdate = FROM_UNIXTIME(".$last_record_postdate."), last_updated = now() WHERE ID = %d", $groupArr['ID'])); //Set group's last postdate
$timeGroup = number_format(microtime(true) - $this->startGroup, 2);
echo "Group processed in $timeGroup seconds $n $n";
}
else
{
echo "No new records for ".$data["group"]." (first $first last $last total $total) grouplast ".$groupArr['last_record'].$n.$n;
}
if ($blnDoDisconnect)
{
$nntp->doQuit();
}
/*
* Got through the updating of this group successfully
* so commit and re-enable the auto committing.
*/
$this->db->commit(); //For anything not yet committed.
$this->db->enableAutoCommit();
}
/**
* Download a range of usenet messages. Store binaries with subjects matching a
* specific pattern in the database.
*/
function scan($nntp, $groupArr, $first, $last, $type='update')
{
$n = $this->n;
$this->startHeaders = microtime(true);
if ($this->compressedHeaders)
$msgs = $nntp->getXOverview($first."-".$last, true, false);
else
$msgs = $nntp->getOverview($first."-".$last, true, false);
if (PEAR::isError($msgs) && $msgs->code == 400)
{
echo "NNTP connection timed out. Reconnecting...$n";
$nntp->doConnect();
$nntp->selectGroup($groupArr['name']);
if ($this->compressedHeaders)
$msgs = $nntp->getXOverview($first."-".$last, true, false);
else
$msgs = $nntp->getOverview($first."-".$last, true, false);
}
$rangerequested = range($first, $last);
$msgsreceived = array();
$msgsblacklisted = array();
$msgsignored = array();
$msgsinserted = array();
$msgsnotinserted = array();
$timeHeaders = number_format(microtime(true) - $this->startHeaders, 2);
if(PEAR::isError($msgs))
{
echo "Error {$msgs->code}: {$msgs->message}$n";
echo "Skipping group$n";
return false;
}
$this->startUpdate = microtime(true);
if (is_array($msgs))
{
//loop headers, figure out parts
foreach($msgs AS $msg)
{
if (!isset($msg['Number']))
continue;
$msgsreceived[] = $msg['Number'];
$msgPart = $msgTotalParts = 0;
$pattern = '|\((\d+)[\/](\d+)\)|i';
preg_match_all($pattern, $msg['Subject'], $matches, PREG_PATTERN_ORDER);
$matchcnt = sizeof($matches[0]);
for ($i=0; $i<$matchcnt; $i++)
{
$msgPart = $matches[1][$i];
$msgTotalParts = $matches[2][$i];
}
if (!isset($msg['Subject']) || $matchcnt == 0) // not a binary post most likely.. continue
{
$msgsignored[] = $msg['Number'];
continue;
}
//Filter binaries based on black/white list
if ($this->isBlackListed($msg, $groupArr['name']))
{
$msgsblacklisted[] = $msg['Number'];
continue;
}
if((int)$msgPart > 0 && (int)$msgTotalParts > 0)
{
$subject = utf8_encode(trim(preg_replace('|\('.$msgPart.'[\/]'.$msgTotalParts.'\)|i', '', $msg['Subject'])));
if(!isset($this->message[$subject]))
{
$this->message[$subject] = $msg;
$this->message[$subject]['MaxParts'] = (int) $msgTotalParts;
$this->message[$subject]['Date'] = strtotime($this->message[$subject]['Date']);
}
if((int)$msgPart > 0)
{
$this->message[$subject]['Parts'][(int)$msgPart] = array('Message-ID' => substr($msg['Message-ID'],1,-1), 'number' => $msg['Number'], 'part' => (int)$msgPart, 'size' => $msg['Bytes']);
}
}
}
unset($msg);
unset($msgs);
$count = 0;
$updatecount = 0;
$partcount = 0;
$maxnum = $last;
$rangenotreceived = array_diff($rangerequested, $msgsreceived);
if ($type != 'partrepair')
echo "Received ".sizeof($msgsreceived)." articles of ".($last-$first+1)." requested, ".sizeof($msgsblacklisted)." blacklisted, ".sizeof($msgsignored)." not binaries $n";
if ($type == 'update' && sizeof($msgsreceived) == 0)
{
echo "Error: Server did not return any articles.$n";
echo "Skipping group$n";
return false;
}
if (sizeof($rangenotreceived) > 0) {
switch($type)
{
case 'backfill':
//don't add missing articles
break;
case 'partrepair':
case 'update':
default:
$this->addMissingParts($rangenotreceived, $groupArr['ID']);
break;
}
echo "Server did not return ".count($rangenotreceived)." article(s).$n";
}
if(isset($this->message) && count($this->message))
{
$maxnum = $first;
//insert binaries and parts into database. when binary already exists; only insert new parts
foreach($this->message AS $subject => $data)
{
if(isset($data['Parts']) && count($data['Parts']) > 0 && $subject != '')
{
$binaryHash = md5($subject.$data['From'].$groupArr['ID']);
$res = $this->db->mysqliQueryOneRow(sprintf("SELECT ID FROM binaries WHERE binaryhash = %s", $this->db->escapeString($binaryHash)));
if(!$res)
{
$sql = sprintf("INSERT INTO binaries (name, fromname, date, xref, totalparts, groupID, binaryhash, dateadded) VALUES (%s, %s, FROM_UNIXTIME(%s), %s, %s, %d, %s, now())", $this->db->escapeString($subject), $this->db->escapeString($data['From']), $this->db->escapeString($data['Date']), $this->db->escapeString($data['Xref']), $this->db->escapeString($data['MaxParts']), $groupArr['ID'], $this->db->escapeString($binaryHash));
$binaryID = $this->db->mysqliQueryInsert($sql);
$count++;
if ($count%500==0) echo "$count bin adds...";
}
else
{
$binaryID = $res["ID"];
$updatecount++;
if ($updatecount%500==0) echo "$updatecount bin updates...";
}
foreach($data['Parts'] AS $partdata)
{
$maxnum = ($partdata['number'] > $maxnum) ? $partdata['number'] : $maxnum;
$partcount++;
$pidata = $this->db->mysqliQueryInsert(sprintf("INSERT INTO parts (binaryID, messageID, number, partnumber, size, dateadded) VALUES (%d, %s, %s, %s, %s, now())", $binaryID, $this->db->escapeString($partdata['Message-ID']), $this->db->escapeString($partdata['number']), $this->db->escapeString(round($partdata['part'])), $this->db->escapeString($partdata['size'])), false);
if (!$pidata) {
$msgsnotinserted[] = $partdata['number'];
} else {
$msgsinserted[] = $partdata['number'];
}
}
}
}
//TODO: determine whether to add to missing articles if insert failed
if (sizeof($msgsnotinserted) > 0)
{
echo 'WARNING: ' . count($msgsnotinserted) . ' Parts failed to insert'.$n;
$this->addMissingParts($msgsnotinserted, $groupArr['ID']);
}
if (($count >= 500) || ($updatecount >= 500)) { echo $n; } //line break for bin adds output
}
$timeUpdate = number_format(microtime(true) - $this->startUpdate, 2);
$timeLoop = number_format(microtime(true)-$this->startLoop, 2);
if ($type != 'partrepair')
{
echo number_format($count).' new, '.number_format($updatecount).' updated, '.number_format($partcount).' parts.';
echo " $timeHeaders headers, $timeUpdate update, $timeLoop range.$n";
}
unset($this->message);
unset($data);
return $maxnum;
}
else
{
echo "Error: Can't get parts from server (msgs not array) $n";
echo "Skipping group$n";
return false;
}
}
/**
* Go through all rows in partrepair table and see if theyve arrived on usenet yet.
*/
private function partRepair($nntp, $groupArr)
{
$n = $this->n;
//get all parts in partrepair table
$missingParts = $this->db->mysqliQuery(sprintf("SELECT * FROM partrepair WHERE groupID = %d AND attempts < 5 ORDER BY numberID ASC LIMIT 30000", $groupArr['ID']));
$partsRepaired = $partsFailed = 0;
if (sizeof($missingParts) > 0)
{
echo 'Attempting to repair '.sizeof($missingParts).' parts...';
//loop through each part to group into ranges
$ranges = array();
$lastnum = $lastpart = 0;
foreach($missingParts as $part)
{
if (($lastnum+1) == $part['numberID']) {
$ranges[$lastpart] = $part['numberID'];
} else {
$lastpart = $part['numberID'];
$ranges[$lastpart] = $part['numberID'];
}
$lastnum = $part['numberID'];
}
//download missing parts in ranges
foreach($ranges as $partfrom=>$partto)
{
$this->startLoop = microtime(true);
echo ".";
//get article from newsgroup
$this->scan($nntp, $groupArr, $partfrom, $partto, 'partrepair');
//check if the articles were added
$articles = implode(',', range($partfrom, $partto));
$sql = sprintf("SELECT pr.ID, pr.numberID, p.number from partrepair pr LEFT JOIN parts p ON p.number = pr.numberID WHERE pr.groupID=%d AND pr.numberID IN (%s) ORDER BY pr.numberID ASC", $groupArr['ID'], $articles);
$result = $this->db->mysqliQueryDirect($sql);
while ($r = mysqli_fetch_assoc($result))
{
if (isset($r['number']) && $r['number'] == $r['numberID'])
{
$partsRepaired++;
//article was added, delete from partrepair
$this->db->mysqliQuery(sprintf("DELETE FROM partrepair WHERE ID=%d", $r['ID']));
}
else
{
$partsFailed++;
//article was not added, increment attempts
$this->db->mysqliQuery(sprintf("UPDATE partrepair SET attempts=attempts+1 WHERE ID=%d", $r['ID']));
}
}
}
echo $n.$partsRepaired.' parts repaired.'.$n;
}
//remove articles that we cant fetch after 5 attempts
$this->db->mysqliQuery(sprintf("DELETE FROM partrepair WHERE attempts >= 5 AND groupID = %d", $groupArr['ID']));
}
/**
* Insert a missing part to the database.
*/
private function addMissingParts($numbers, $groupID)
{
$added = false;
$insertStr = "INSERT INTO partrepair (numberID, groupID) VALUES ";
foreach($numbers as $number)
{
if ($number > 0)
{
$added = true;
$insertStr .= sprintf("(%u, %d), ", $number, $groupID);
}
}
if ($added)
{
$insertStr = substr($insertStr, 0, -2);
$insertStr .= " ON DUPLICATE KEY UPDATE attempts=attempts+1";
return $this->db->mysqliQueryInsert($insertStr, false);
}
return -1;
}
/**
* Return internally cached list of binary blacklist patterns.
*/
public function retrieveBlackList()
{
if (is_array($this->blackList) && !empty($this->blackList)) { return $this->blackList; }
$blackList = $this->getBlacklist(true);
$this->blackList = $blackList;
return $blackList;
}
/**
* Test if a message subject is blacklisted.
*/
public function isBlackListed($msg, $groupName)
{
$blackList = $this->retrieveBlackList();
$field = array();
if (isset($msg["Subject"]))
$field[Binaries::BLACKLIST_FIELD_SUBJECT] = $msg["Subject"];
if (isset($msg["From"]))
$field[Binaries::BLACKLIST_FIELD_FROM] = $msg["From"];
if (isset($msg["Message-ID"]))
$field[Binaries::BLACKLIST_FIELD_MESSAGEID] = $msg["Message-ID"];
foreach ($blackList as $blist)
{
if (preg_match('/^'.$blist['groupname'].'$/i', $groupName))
{
//blacklist
if ($blist['optype'] == 1)
{
if (preg_match('/'.$blist['regex'].'/i', $field[$blist['msgcol']])) {
return true;
}
}
else if ($blist['optype'] == 2)
{
if (!preg_match('/'.$blist['regex'].'/i', $field[$blist['msgcol']])) {
return true;
}
}
}
}
return false;
}
/**
* Rawsearch. Perform a simple like match on binary subjects matching a pattern.
*/
public function search($search, $limit=1000, $excludedcats=array())
{
//
// if the query starts with a ^ it indicates the search is looking for items which start with the term
// still do the like match, but mandate that all items returned must start with the provided word
//
$words = explode(" ", $search);
$searchsql = "";
$intwordcount = 0;
if (count($words) > 0)
{
foreach ($words as $word)
{
//
// see if the first word had a caret, which indicates search must start with term
//
if ($intwordcount == 0 && (strpos($word, "^") === 0))
$searchsql.= sprintf(" and b.name like %s", $this->db->escapeString(substr($word, 1)."%"));
else
$searchsql.= sprintf(" and b.name like %s", $this->db->escapeString("%".$word."%"));
$intwordcount++;
}
}
$exccatlist = "";
if (count($excludedcats) > 0)
$exccatlist = " and b.categoryID not in (".implode(",", $excludedcats).") ";
$res = $this->db->mysqliQuery(sprintf("
SELECT b.*,
g.name AS group_name,
r.guid,
(SELECT COUNT(ID) FROM parts p where p.binaryID = b.ID) as 'binnum'
FROM binaries b
INNER JOIN groups g ON g.ID = b.groupID
LEFT OUTER JOIN releases r ON r.ID = b.releaseID
WHERE 1=1 %s %s order by DATE DESC LIMIT %d ",
$searchsql, $exccatlist, $limit));
return $res;
}
/**
* Get all binaries for a release.
*/
public function getForReleaseId($id)
{
return $this->db->mysqliQuery(sprintf("select binaries.* from binaries where releaseID = %d order by relpart", $id));
}
/**
* Get a binary row.
*/
public function getById($id)
{
return $this->db->mysqliQueryOneRow(sprintf("select binaries.*, groups.name as groupname from binaries left outer join groups on binaries.groupID = groups.ID where binaries.ID = %d ", $id));
}
/**
* Get list of blacklists from database.
*/
public function getBlacklist($activeonly=true)
{
$where = "";
if ($activeonly)
$where = " where binaryblacklist.status = 1 ";
return $this->db->mysqliQuery("SELECT binaryblacklist.ID, binaryblacklist.optype, binaryblacklist.status, binaryblacklist.description, binaryblacklist.groupname AS groupname, binaryblacklist.regex,
groups.ID AS groupID, binaryblacklist.msgcol FROM binaryblacklist
left outer JOIN groups ON groups.name = binaryblacklist.groupname
".$where."
ORDER BY coalesce(groupname,'zzz')");
}
/**
* Get a blacklist row from database.
*/
public function getBlacklistByID($id)
{
return $this->db->mysqliQueryOneRow(sprintf("select * from binaryblacklist where ID = %d ", $id));
}
/**
* Delete a blacklist row from database.
*/
public function deleteBlacklist($id)
{
return $this->db->mysqliQuery(sprintf("delete from binaryblacklist where ID = %d", $id));
}
/**
* Update a blacklist row.
*/
public function updateBlacklist($regex)
{
$groupname = $regex["groupname"];
if ($groupname == "")
$groupname = "null";
else
{
$groupname = preg_replace("/a\.b\./i", "alt.binaries.", $groupname);
$groupname = sprintf("%s", $this->db->escapeString($groupname));
}
$this->db->mysqliQuery(sprintf("update binaryblacklist set groupname=%s, regex=%s, status=%d, description=%s, optype=%d, msgcol=%d where ID = %d ", $groupname, $this->db->escapeString($regex["regex"]), $regex["status"], $this->db->escapeString($regex["description"]), $regex["optype"], $regex["msgcol"], $regex["id"]));
}
/**
* Add a new blacklist row.
*/
public function addBlacklist($regex)
{
$groupname = $regex["groupname"];
if ($groupname == "")
$groupname = "null";
else
{
$groupname = preg_replace("/a\.b\./i", "alt.binaries.", $groupname);
$groupname = sprintf("%s", $this->db->escapeString($groupname));
}
return $this->db->mysqliQueryInsert(sprintf("insert into binaryblacklist (groupname, regex, status, description, optype, msgcol) values (%s, %s, %d, %s, %d, %d) ",
$groupname, $this->db->escapeString($regex["regex"]), $regex["status"], $this->db->escapeString($regex["description"]), $regex["optype"], $regex["msgcol"]));
}
/**
* Add a new binary row and its associated parts.
*/
public function delete($id)
{
$this->db->mysqliQuery(sprintf("delete from parts where binaryID = %d", $id));
$this->db->mysqliQuery(sprintf("delete from binaries where ID = %d", $id));
}
}
-3
View File
@@ -1,3 +0,0 @@
<?php
require_once(dirname(__FILE__)."/../../../config.php");
?>
-18
View File
@@ -1,18 +0,0 @@
<?php
require_once("/var/www/newznab/www/config.php");
require_once("/var/www/newznab/www/lib/framework/db.php");
$db = new DB();
echo "This script will convert only your parts and binaries table to InnoDB with compressed row format..\n";
echo "This may take a while depending on how large your tables are. Do not stop this script. It will finish.\n\n";
echo "Converting the binaries table to InnoDB with compressed row format.\n";
$db->query("ALTER TABLE binaries ENGINE=InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;");
echo "Finished converting the binaries table to InnoDB with compressed row format..\n\n";
echo "Converting the parts table to InnoDB with compressed row format.\n";
$db->query("ALTER TABLE parts ENGINE=InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;");
echo "Finished converting the parts table to InnoDB with compressed row format..\n\n";
?>
-312
View File
@@ -1,312 +0,0 @@
<?php
require_once("innodb/binaries.php");
require_once("framework/db.php");
require_once(WWW_DIR."/lib/Net_NNTP/NNTP/Client.php");
/**
* This class extends the standard PEAR NNTP class with some extra features.
*/
class Nntp extends Net_NNTP_Client
{
/**
* Start an NNTP connection.
*/
function doConnect()
{
$enc = false;
if (defined("NNTP_SSLENABLED") && NNTP_SSLENABLED == true)
$enc = 'ssl';
$ret = $this->connect(NNTP_SERVER, $enc, NNTP_PORT);
if(PEAR::isError($ret))
{
echo "Cannot connect to server ".NNTP_SERVER.(!$enc?" (nonssl) ":"(ssl) ").": ".$ret->getMessage();
die();
}
if(!defined(NNTP_USERNAME) && NNTP_USERNAME!="" )
{
$ret2 = $this->authenticate(NNTP_USERNAME, NNTP_PASSWORD);
if(PEAR::isError($ret2))
{
echo "Cannot authenticate to server ".NNTP_SERVER.(!$enc?" (nonssl) ":" (ssl) ")." - ".NNTP_USERNAME." (".$ret2->getMessage().")";
die();
}
}
}
/**
* End an NNTP connection.
*/
function doQuit()
{
$this->quit();
}
/**
* Retrieve an NNTP message and decode it.
*/
function getMessage($groupname, $partMsgId)
{
$summary = $this->selectGroup($groupname);
$message = $dec = '';
if (PEAR::isError($summary))
{
echo "NntpPrc : ".substr($summary->getMessage(), 0, 30)."\n";
return false;
}
$body = $this->getBody('<'.$partMsgId.'>', true);
if (PEAR::isError($body))
{
//echo 'NntpPrc : Error fetching part number '.$partMsgId.' in '.$groupname.' (Server response: '. $body->getMessage().')\n';
return false;
}
$message = $this->decodeYenc($body);
if (!$message)
{
//
// Yenc decode failed
//
return false;
}
echo $message . "\n";
return $message;
}
/**
* Retrieve a series of NNTP messages and decode them.
*/
function getMessages($groupname, $msgIds)
{
$summary = $this->selectGroup($groupname);
$message = $dec = '';
if (PEAR::isError($summary))
{
echo "NntpPrc : ".substr($summary->getMessage(), 0, 30)."\n";
return false;
}
foreach($msgIds as $msgId)
{
$messageID = '<'.$msgId.'>';
$body = $this->getBody($messageID, true);
if (PEAR::isError($body))
{
//echo 'NntpPrc : Error fetching '.$messageID.' in '.$groupname.' (Server response: '. $body->getMessage().')';
return false;
}
$dec = $this->decodeYenc($body);
if (!$dec)
{
//
// Yenc decode failed
//
return false;
}
$message .= $dec;
}
return $message;
}
/**
* Retrieve all NNTP messages associated with a binaries.ID
*/
function getBinary($binaryId, $isNfo=false)
{
$db = new DB();
$bin = new Binaries();
$binary = $bin->getById($binaryId);
if (!$binary)
return false;
$summary = $this->selectGroup($binary['groupname']);
$message = $dec = '';
if (PEAR::isError($summary))
{
echo "NntpPrc : ".substr($summary->getMessage(), 0, 30)."\n";
return false;
}
$resparts = $db->query(sprintf("SELECT size, partnumber, messageID FROM parts WHERE binaryID = %d ORDER BY partnumber", $binaryId));
//
// Dont attempt to download nfos which are larger than one part.
//
if (sizeof($resparts) > 1 && $isNfo === true)
{
return false;
}
foreach($resparts as $part)
{
$messageID = '<'.$part['messageID'].'>';
$body = $this->getBody($messageID, true);
if (PEAR::isError($body))
{
//echo 'NntpPrc : Error fetching part number '.$part['messageID'].' in '.$binary['groupname'].' (Server response: '. $body->getMessage().')';
return false;
}
$dec = $this->decodeYenc($body);
if (!$dec)
{
//
// Yenc decode failed
//
return false;
}
$message .= $dec;
}
return $message;
}
/**
* Get XZVER for a range of NNTP messages.
*/
function getXOverview($range, $_names = true, $_forceNames = true)
{
// Fetch overview from server
$overview = $this->cmdXZver($range);
if (PEAR::isError($overview)) {
return $overview;
}
// Use field names from overview format as keys?
if ($_names)
{
// Already cached?
if (is_null($this->_overviewFormatCache)) {
// Fetch overview format
$format = $this->getOverviewFormat($_forceNames, true);
if (PEAR::isError($format)){
return $format;
}
// Prepend 'Number' field
$format = array_merge(array('Number' => false), $format);
// Cache format
$this->_overviewFormatCache = $format;
}
else
{
$format = $this->_overviewFormatCache;
}
// Loop through all articles
foreach ($overview as $key => $article)
{
if (sizeof($format) == sizeof($article))
{
//Replace overview using $format as keys, $article as values
$overview[$key] = array_combine(array_keys($format), $article);
// If article prefixed by field name, remove it
foreach($format as $fkey=>$fval)
{
if ($fval === true)
{
$overview[$key][$fkey] = trim(str_replace($fkey.':', '', $overview[$key][$fkey]));
}
}
}
}
}
switch (true)
{
// Expect one article
case is_null($range);
case is_int($range);
case is_string($range) && ctype_digit($range):
case is_string($range) && substr($range, 0, 1) == '<' && substr($range, -1, 1) == '>':
if (count($overview) == 0) {
return false;
} else {
return reset($overview);
}
break;
// Expect multiple articles
default:
return $overview;
}
}
/**
* Send XZVER command over NNTP connection.
*/
function cmdXZver($range = NULL)
{
if (is_null($range))
$command = 'XZVER';
else
$command = 'XZVER ' . $range;
$response = $this->_sendCommand($command);
switch ($response) {
case 224: // 224, RFC2980: 'Overview information follows'
$data = $this->_getTextResponse();
//de-yenc
$dec = $this->decodeYenc(implode("\r\n", $data));
if (!$dec)
{
$this->throwError("yenc decode failure");
}
//inflate deflated string
$data = explode("\r\n", gzinflate($dec));
foreach ($data as $key => $value)
$data[$key] = explode("\t", ltrim($value));
return $data;
break;
case 412: // 412, RFC2980: 'No news group current selected'
$this->throwError("No news group current selected ({$this->_currentStatusResponse()})", $response);
break;
case 420: // 420, RFC2980: 'No article(s) selected'
$this->throwError("No article(s) selected ({$this->_currentStatusResponse()})", $response);
break;
case 502: // 502 RFC2980: 'no permission'
$this->throwError("No permission ({$this->_currentStatusResponse()})", $response);
break;
case 500: // 500 RFC2980: 'unknown command'
$this->throwError("XZver not supported ({$this->_currentStatusResponse()})", $response);
break;
default:
return $this->_handleUnexpectedResponse($response);
}
}
/**
* Decode a yenc encoded string.
*/
function decodeYenc($yencodedvar)
{
$input = array();
preg_match("/^(=ybegin.*=yend[^$]*)$/ims", $yencodedvar, $input);
if (isset($input[1]))
{
$ret = "";
$input = trim(preg_replace("/\r\n/im", "", preg_replace("/(^=yend.*)/im", "", preg_replace("/(^=ypart.*\\r\\n)/im", "", preg_replace("/(^=ybegin.*\\r\\n)/im", "", $input[1], 1), 1), 1)));
for( $chr = 0; $chr < strlen($input) ; $chr++)
$ret .= ($input[$chr] != "=" ? chr(ord($input[$chr]) - 42) : chr((ord($input[++$chr]) - 64) - 42));
return $ret;
}
return false;
}
}
-174
View File
@@ -1,174 +0,0 @@
<?php
declare(ticks = 1);
/**
* Class that wraps PHP POSIX and PCNTL functions to easily implement
* process forking and pseudo-threading
*
* @author Don Bauer <lordgnu@me.com>
* @link https://github.com/lordgnu/PowerSpawn
* @version 1.0
*/
class PowerSpawn
{
private $myChildren;
private $parentPID;
private $shutdownCallback = null;
private $killCallback = null;
public $maxChildren = 10; // Max number of children allowed to Spawn
public $timeLimit = 0; // Time limit in seconds (0 to disable)
public $sleepCount = 1; // Number of seconds to sleep on Tick()
public $childData; // Variable for storage of data to be passed to the next spawned child
public $complete;
public function __construct() {
if (function_exists('pcntl_fork') && function_exists('posix_getpid')) {
// Everything is good
$this->parentPID = $this->myPID();
$this->myChildren = array();
$this->complete = false;
// Install the signal handler
pcntl_signal(SIGCHLD, array($this, 'sigHandler'));
} else {
die("You must have POSIX and PCNTL functions to use PowerSpawn\n");
}
}
public function __destruct() {
}
public function sigHandler($signo) {
switch ($signo) {
case SIGCHLD:
$this->checkChildren();
break;
}
}
public function checkChildren() {
foreach ($this->myChildren as $i => $child) {
// Check for time running and if still running
if ($this->pidDead($child['pid']) != 0) {
// Child is dead
unset($this->myChildren[$i]);
} elseif ($this->timeLimit > 0) {
// Check the time limit
if (time() - $child['time'] >= $this->timeLimit) {
// Child had exceeded time limit
$this->killChild($child['pid']);
unset($this->myChildren[$i]);
}
}
}
}
public function myPID() {
return posix_getpid();
}
public function myParent() {
return posix_getppid();
}
public function spawnChild() {
$time = time();
$pid = pcntl_fork();
if ($pid) $this->myChildren[] = array('time'=>$time,'pid'=>$pid);
}
public function killChild($pid = 0) {
if ($pid > 0) {
posix_kill($pid, SIGTERM);
if ($this->killCallback !== null) call_user_func($this->killCallback);
}
}
public function parentCheck() {
if ($this->myPID() == $this->parentPID) {
return true;
} else {
return false;
}
}
public function pidDead($pid = 0) {
if ($pid > 0) {
return pcntl_waitpid($pid, $status, WUNTRACED OR WNOHANG);
} else {
return 0;
}
}
public function setCallback($callback = null) {
$this->shutdownCallback = $callback;
}
public function setKillCallback($callback = null) {
$this->killCallback = $callback;
}
public function childCount() {
return count($this->myChildren);
}
public function runParentCode() {
if (!$this->complete) {
return $this->parentCheck();
} else {
if ($this->shutdownCallback !== null)
call_user_func($this->shutdownCallback);
return false;
}
}
public function runChildCode() {
return !$this->parentCheck();
}
public function spawnReady() {
if (count($this->myChildren) < $this->maxChildren) {
return true;
} else {
return false;
}
}
public function shutdown() {
while($this->childCount()) {
$this->checkChildren();
$this->tick();
}
$this->complete = true;
}
public function tick() {
sleep($this->sleepCount);
}
}
/*
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
https://github.com/lordgnu/PowerSpawn for usage examples
*/
-146
View File
@@ -1,146 +0,0 @@
<?php
require_once("framework/db.php");
class Sites
{
const REGISTER_STATUS_OPEN = 0;
const REGISTER_STATUS_INVITE = 1;
const REGISTER_STATUS_CLOSED = 2;
const ERR_BADUNRARPATH = -1;
const ERR_BADFFMPEGPATH = -2;
const ERR_BADMEDIAINFOPATH = -3;
const ERR_BADNZBPATH = -4;
const ERR_DEEPNOUNRAR = -5;
const ERR_BADTMPUNRARPATH = -6;
const ERR_BADLAMEPATH = -7;
const ERR_SABCOMPLETEPATH = -8;
public function version()
{
return "0.2.3p";
}
public function update($form)
{
$db = new DB();
$site = $this->row2Object($form);
if (substr($site->nzbpath, strlen($site->nzbpath) - 1) != '/')
$site->nzbpath = $site->nzbpath."/";
//
// Validate site settings
//
if ($site->mediainfopath != "" && !is_file($site->mediainfopath))
return Sites::ERR_BADMEDIAINFOPATH;
if ($site->ffmpegpath != "" && !is_file($site->ffmpegpath))
return Sites::ERR_BADFFMPEGPATH;
if ($site->unrarpath != "" && !is_file($site->unrarpath))
return Sites::ERR_BADUNRARPATH;
if ($site->nzbpath != "" && !file_exists($site->nzbpath))
return Sites::ERR_BADNZBPATH;
if ($site->checkpasswordedrar == 2 && !is_file($site->unrarpath))
return Sites::ERR_DEEPNOUNRAR;
if ($site->tmpunrarpath != "" && !file_exists($site->tmpunrarpath))
return Sites::ERR_BADTMPUNRARPATH;
if ($site->lamepath != "" && !file_exists($site->lamepath))
return Sites::ERR_BADLAMEPATH;
if ($site->sabcompletedir != "" && !file_exists($site->sabcompletedir))
return Sites::ERR_SABCOMPLETEPATH;
$sql = $sqlKeys = array();
foreach($form as $settingK=>$settingV)
{
$sql[] = sprintf("WHEN %s THEN %s", $db->escapeString($settingK), $db->escapeString(trim($settingV)));
$sqlKeys[] = $db->escapeString($settingK);
}
$db->query(sprintf("UPDATE site SET value = CASE setting %s END WHERE setting IN (%s)", implode(' ', $sql), implode(', ', $sqlKeys)));
return $site;
}
public function get()
{
$db = new DB();
$rows = $db->query("select * from site");
if ($rows === false)
return false;
return $this->rows2Object($rows);
}
public function rows2Object($rows)
{
$obj = new stdClass;
foreach($rows as $row)
$obj->{$row['setting']} = $row['value'];
$obj->{'version'} = $this->version();
return $obj;
}
public function row2Object($row)
{
$obj = new stdClass;
$rowKeys = array_keys($row);
foreach($rowKeys as $key)
$obj->{$key} = $row[$key];
return $obj;
}
public function getUnappliedPatches($site)
{
preg_match("/\d+/", $site->dbversion, $matches);
$currentrev = $matches[0];
$patchpath = WWW_DIR."../db/patch/0.2.3/";
$patchfiles = glob($patchpath."*.sql");
$missingpatch = array();
foreach($patchfiles as $file)
{
$filecontents = file_get_contents($file);
if (preg_match("/Rev\: (\d+)/", $filecontents, $matches))
{
$patchrev = $matches[1];
if ($patchrev > $currentrev)
$missingpatch[] = $file;
}
}
return $missingpatch;
}
public function updateItem($setting, $value)
{
$db = new DB();
$sql = sprintf("update site set value = %s where setting = %s", $db->escapeString($value), $db->escapeString($setting));
return $db->query($sql);
}
public function updateLatestRegexRevision($rev)
{
return $this->updateItem("latestregexrevision", $rev);
}
public function getLicense($html=false)
{
$n = "\r\n";
if ($html)
$n = "<br/>";
return $n."newznab ".$this->version()." Copyright (C) ".date("Y")." newznab.com".$n."
This program is distributed with a commercial licence. See LICENCE.txt for
further details.".$n;
}
}
-143
View File
@@ -1,143 +0,0 @@
<?php
require_once("config.php");
require_once("lib/framework/db.php");
$db = new DB();
$retval = "";
if ($argc == 1) {
echo "no arguments specified - php nzb-import.php /path/to/nzb bool_use_filenames\n";
return;
}
$filestoprocess = Array();
$browserpostednames = Array();
$strTerminator = "\n";
$path = $argv[1];
$usenzbname = (isset($argv[2]) && $argv[2] == 'true') ? true : false;
if (substr($path, strlen($path) - 1) != '/')
$path = $path . "/";
$groups = $db->query("SELECT ID, name FROM groups");
foreach ($groups as $group)
$siteGroups[$group["name"]] = $group["ID"];
if (!isset($groups) || count($groups) == 0) {
echo "no groups available in the database, add first.\n";
} else {
$nzbCount = 0;
if (count($filestoprocess) == 0)
$filestoprocess = glob($path . "*.nzb");
$start = date('Y-m-d H:i:s');
foreach ($filestoprocess as $nzbFile) {
$importfailed = false;
$transaction = "";
$nzb = file_get_contents($nzbFile);
$xml = @simplexml_load_string($nzb);
if (!$xml || strtolower($xml->getName()) != 'nzb') {
continue;
}
$i = 0;
foreach ($xml->file as $file) {
//file info
$groupID = -1;
$name = (string)$file->attributes()->subject;
$fromname = (string)$file->attributes()->poster;
$unixdate = (string)$file->attributes()->date;
$date = date("Y-m-d H:i:s", (string)$file->attributes()->date);
//groups
$groupArr = array();
foreach ($file->groups->group as $group) {
$group = (string)$group;
if (array_key_exists($group, $siteGroups)) {
$groupID = $siteGroups[$group];
}
$groupArr[] = $group;
}
if ($groupID != -1) {
$xref = implode(': ', $groupArr) . ':';
$totalParts = sizeof($file->segments->segment);
//insert binary
$binaryHash = md5($name . $fromname . $groupID);
if ($usenzbname) { //Use the binary names
$usename = str_replace('.nzb', '', basename($nzbFile));
$binarySql = sprintf("INSERT INTO binaries (name, fromname, date, xref, totalParts, groupID, binaryhash, dateadded, importname, relname, relpart, reltotalpart, procstat, categoryID, regexID, reqID) values (%s, %s, %s, %s, %s, %s, %s, NOW(), %s, replace(%s, '_', ' '), %d, %d, %d, %s, %d, %s);",
$db->escapeString($name), $db->escapeString($fromname), $db->escapeString($date),
$db->escapeString($xref), $db->escapeString($totalParts), $db->escapeString($groupID), $db->escapeString($binaryHash), $db->escapeString($nzbFile),
$db->escapeString($usename), 1, 1, 5, "null", "null", "null");
$transaction = $transaction . $binarySql;
} else { //Don't use the binary names
$binarySql = sprintf("INSERT INTO binaries (name, fromname, date, xref, totalParts, groupID, binaryhash, dateadded, importname) values (%s, %s, %s, %s, %s, %s, %s, NOW(), %s);",
$db->escapeString($name), $db->escapeString($fromname), $db->escapeString($date),
$db->escapeString($xref), $db->escapeString($totalParts), $db->escapeString($groupID), $db->escapeString($binaryHash), $db->escapeString($nzbFile));
$transaction = $transaction . $binarySql;
}
$sessionVarSQL = "SET @binID = last_insert_id();";
$transaction = $transaction . $sessionVarSQL;
//segments (i.e. parts)
if (count($file->segments->segment) > 0) {
$partsSql = "INSERT INTO parts (binaryID, messageID, number, partnumber, size, dateadded) values ";
foreach ($file->segments->segment as $segment) {
$messageId = (string)$segment;
$partnumber = $segment->attributes()->number;
$size = $segment->attributes()->bytes;
$partsSql .= sprintf("(%s, %s, 0, %s, %s, NOW()),",
"@binID", $db->escapeString($messageId), $db->escapeString($partnumber),
$db->escapeString($size));
}
$partsSql = substr($partsSql, 0, -1) . ";";
$transaction = $transaction . $partsSql;
}
} else {
$importfailed = true;
echo ("no group found for " . $name . " (one of " . implode(', ', $groupArr) . " are missing)" . $strTerminator);
flush();
break;
}
}
if (!$importfailed) {
$db->multiQueryTransaction($transaction);
$nzbCount++;
@unlink($nzbFile);
echo ("imported " . $nzbFile . $strTerminator);
flush();
}
if ($nzbCount == 100) {
break;
}
}
}
$seconds = strtotime(date('Y-m-d H:i:s')) - strtotime($start);
$retval .= 'Processed ' . $nzbCount . ' nzbs in ' . $seconds . ' second(s)';
echo $retval;
die();
-24
View File
@@ -1,24 +0,0 @@
<?php
require("config.php");
require_once("lib/groups.php");
require_once("lib/innodb/binaries.php");
if (isset($argv[1]))
{
$group = $argv[1];
echo "Updating group {$group}\n";
$g = new Groups;
$group = $g->getByName($group);
$bin = new Binaries;
$bin->updateGroup(null, $group);
}
else
{
$binaries = new Binaries;
$binaries->updateAllGroups();
}
?>
-77
View File
@@ -1,77 +0,0 @@
<?php
require("config.php");
require_once(dirname(__FILE__)."/lib/framework/db.php");
require_once(dirname(__FILE__)."/lib/groups.php");
require_once(dirname(__FILE__)."/lib/innodb/binaries.php");
require_once(dirname(__FILE__)."/lib/powerspawn.php");
$groups = new Groups;
$groupList = $groups->getActive();
unset($groups);
$ps = new PowerSpawn;
$ps->setCallback('psUpdateComplete');
$ps->maxChildren = 10;
$ps->timelimit = 0; // Disable child timeout
echo "Starting threaded binary update process\n";
while ($ps->runParentCode())
{
// Start the parent loop
if (count($groupList))
{
// We still have groups to process
if ($ps->spawnReady())
{
// Spawn another thread
$ps->childData = array_pop($groupList);
echo "[Thread-MASTER] Spawning new thread. Still have " . count($groupList) ." group(s) to update after this\n";
$ps->spawnChild();
}
else
{
// There are no more slots available to run
$ps->tick();
#echo "- \n";
}
}
else
{
// No more groups to process
echo "No more groups to process - Initiating shutdown\n";
$ps->shutdown();
echo "Shutdown complete\n";
}
}
unset($groupList);
if ($ps->runChildCode())
{
$group = $ps->childData;
$thread = sprintf("%05d",$ps->myPID());
echo "[Thread-{$thread}] Begining processing for group {$group['name']}\n";
$param = $group['name'];
$dir = dirname(__FILE__);
$file = 'update_binaries.php';
$output = shell_exec("php {$dir}/{$file} {$param}");
echo "[Thread-{$thread}] Completed update for group {$group['name']}\n";
}
// Exit to call back to parent - Let know that child has completed
exit(0);
// Create callback function
function psUpdateComplete()
{
echo "[Thread-MASTER] Threaded update process complete\n";
}
?>
-15
View File
@@ -1,15 +0,0 @@
<?php
$newzpath = getenv('NEWZPATH');
require_once("$newzpath/www/config.php");
require_once(WWW_DIR."/lib/backfill.php");
if (isset($argv[1]))
$groupName = $argv[1];
else
$groupName = '';
$backfill = new Backfill();
$backfill->backfillAllGroups($groupName);
?>
-77
View File
@@ -1,77 +0,0 @@
<?php
$newzpath = getenv('NEWZPATH');
require_once("$newzpath/www/config.php");
require_once(WWW_DIR."/lib/groups.php");
require_once(WWW_DIR."/lib/binaries.php");
require_once("powerspawn.php");
$groups = new Groups;
$groupList = $groups->getActive();
unset($groups);
$ps = new PowerSpawn;
$ps->setCallback('psUpdateComplete');
$ps->maxChildren = 10;
$ps->timeLimit = 0; // Disable child timeout
echo "Starting threaded backfill process\n";
while ($ps->runParentCode())
{
// Start the parent loop
if (count($groupList))
{
// We still have groups to process
if ($ps->spawnReady())
{
// Spawn another thread
$ps->childData = array_pop($groupList);
echo "[Thread-MASTER] Spawning new thread. Still have " . count($groupList) ." group(s) to update after this\n";
$ps->spawnChild();
}
else
{
// There are no more slots available to run
$ps->tick();
#echo ". \n";
}
}
else
{
// No more groups to process
echo "No more groups to process - Initiating shutdown\n";
$ps->shutdown();
echo "Shutdown complete\n";
}
}
unset($groupList);
if ($ps->runChildCode())
{
$group = $ps->childData;
$thread = sprintf("%05d",$ps->myPID());
echo "[Thread-{$thread}] Begining backfill processing for group {$group['name']}\n";
$param = $group['name'];
$dir = dirname(__FILE__);
$file = 'backfill.php';
$output = shell_exec("php {$dir}/{$file} {$param}");
echo "[Thread-{$thread}] Completed update for group {$group['name']}\n";
}
// Exit to call back to parent - Let know that child has completed
exit(0);
// Create callback function
function psUpdateComplete()
{
echo "[Thread-MASTER] Threaded backfill process complete\n";
}
?>
-174
View File
@@ -1,174 +0,0 @@
<?php
declare(ticks = 1);
/**
* Class that wraps PHP POSIX and PCNTL functions to easily implement
* process forking and pseudo-threading
*
* @author Don Bauer <lordgnu@me.com>
* @link https://github.com/lordgnu/PowerSpawn
* @version 1.0
*/
class PowerSpawn
{
private $myChildren;
private $parentPID;
private $shutdownCallback = null;
private $killCallback = null;
public $maxChildren = 10; // Max number of children allowed to Spawn
public $timeLimit = 0; // Time limit in seconds (0 to disable)
public $sleepCount = 1; // Number of seconds to sleep on Tick()
public $childData; // Variable for storage of data to be passed to the next spawned child
public $complete;
public function __construct() {
if (function_exists('pcntl_fork') && function_exists('posix_getpid')) {
// Everything is good
$this->parentPID = $this->myPID();
$this->myChildren = array();
$this->complete = false;
// Install the signal handler
pcntl_signal(SIGCHLD, array($this, 'sigHandler'));
} else {
die("You must have POSIX and PCNTL functions to use PowerSpawn\n");
}
}
public function __destruct() {
}
public function sigHandler($signo) {
switch ($signo) {
case SIGCHLD:
$this->checkChildren();
break;
}
}
public function checkChildren() {
foreach ($this->myChildren as $i => $child) {
// Check for time running and if still running
if ($this->pidDead($child['pid']) != 0) {
// Child is dead
unset($this->myChildren[$i]);
} elseif ($this->timeLimit > 0) {
// Check the time limit
if (time() - $child['time'] >= $this->timeLimit) {
// Child had exceeded time limit
$this->killChild($child['pid']);
unset($this->myChildren[$i]);
}
}
}
}
public function myPID() {
return posix_getpid();
}
public function myParent() {
return posix_getppid();
}
public function spawnChild() {
$time = time();
$pid = pcntl_fork();
if ($pid) $this->myChildren[] = array('time'=>$time,'pid'=>$pid);
}
public function killChild($pid = 0) {
if ($pid > 0) {
posix_kill($pid, SIGTERM);
if ($this->killCallback !== null) call_user_func($this->killCallback);
}
}
public function parentCheck() {
if ($this->myPID() == $this->parentPID) {
return true;
} else {
return false;
}
}
public function pidDead($pid = 0) {
if ($pid > 0) {
return pcntl_waitpid($pid, $status, WUNTRACED OR WNOHANG);
} else {
return 0;
}
}
public function setCallback($callback = null) {
$this->shutdownCallback = $callback;
}
public function setKillCallback($callback = null) {
$this->killCallback = $callback;
}
public function childCount() {
return count($this->myChildren);
}
public function runParentCode() {
if (!$this->complete) {
return $this->parentCheck();
} else {
if ($this->shutdownCallback !== null)
call_user_func($this->shutdownCallback);
return false;
}
}
public function runChildCode() {
return !$this->parentCheck();
}
public function spawnReady() {
if (count($this->myChildren) < $this->maxChildren) {
return true;
} else {
return false;
}
}
public function shutdown() {
while($this->childCount()) {
$this->checkChildren();
$this->tick();
}
$this->complete = true;
}
public function tick() {
sleep($this->sleepCount);
}
}
/*
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
https://github.com/lordgnu/PowerSpawn for usage examples
*/
-25
View File
@@ -1,25 +0,0 @@
<?php
$newzpath = getenv('NEWZPATH');
require_once("$newzpath/www/config.php");
require_once(WWW_DIR."/lib/groups.php");
require_once(WWW_DIR."/lib/binaries.php");
if (isset($argv[1]))
{
$group = $argv[1];
echo "Updating group {$group}\n";
$g = new Groups;
$group = $g->getByName($group);
$bin = new Binaries;
$bin->updateGroup(null, $group);
}
else
{
$binaries = new Binaries;
$binaries->updateAllGroups();
}
?>
-77
View File
@@ -1,77 +0,0 @@
<?php
$newzpath = getenv('NEWZPATH');
require("$newzpath/www/config.php");
require_once(WWW_DIR."/lib/groups.php");
require_once(WWW_DIR."/lib/binaries.php");
require_once("powerspawn.php");
$groups = new Groups;
$groupList = $groups->getActive();
unset($groups);
$ps = new PowerSpawn;
$ps->setCallback('psUpdateComplete');
$ps->maxChildren = 10;
$ps->timelimit = 0; // Disable child timeout
echo "Starting threaded binary update process\n";
while ($ps->runParentCode())
{
// Start the parent loop
if (count($groupList))
{
// We still have groups to process
if ($ps->spawnReady())
{
// Spawn another thread
$ps->childData = array_pop($groupList);
echo "[Thread-MASTER] Spawning new thread. Still have " . count($groupList) ." group(s) to update after this\n";
$ps->spawnChild();
}
else
{
// There are no more slots available to run
$ps->tick();
#echo "- \n";
}
}
else
{
// No more groups to process
echo "No more groups to process - Initiating shutdown\n";
$ps->shutdown();
echo "Shutdown complete\n";
}
}
unset($groupList);
if ($ps->runChildCode())
{
$group = $ps->childData;
$thread = sprintf("%05d",$ps->myPID());
echo "[Thread-{$thread}] Begining processing for group {$group['name']}\n";
$param = $group['name'];
$dir = dirname(__FILE__);
$file = 'update_binaries.php';
$output = shell_exec("php {$dir}/{$file} {$param}");
echo "[Thread-{$thread}] Completed update for group {$group['name']}\n";
}
// Exit to call back to parent - Let know that child has completed
exit(0);
// Create callback function
function psUpdateComplete()
{
echo "[Thread-MASTER] Threaded update process complete\n";
}
?>
+4 -8
View File
@@ -18,20 +18,19 @@ if [ "$THREADS" == "true" -a "$INNODB" == "true" ]; then
#make active groups current
if [[ $BINARIES == "true" ]] ; then
cd $INNODB_PATH
cd $NEWZNAB_PATH
[ -f update_binaries_threaded.php ] && $PHP update_binaries_threaded.php
fi
#get backfill for all active groups
if [[ $BACKFILL == "true" ]] ; then
cd $INNODB_PATH
cd $NEWZNAB_PATH
[ -f backfill_threaded.php ] && $PHP backfill_threaded.php
fi
wait
if [[ $BACKFILL == "true" ]] ; then
cd $INNODB_PATH
#increment backfill days
$MYSQL -u$DB_USER -h $DB_HOST --password=$DB_PASSWORD $DB_NAME -e "${MYSQL_CMD}"
fi
@@ -65,7 +64,6 @@ elif [ "$THREADS" != "true" -a "$INNODB" == "true" ]; then
wait
if [[ $BACKFILL == "true" ]] ; then
cd $INNODB_PATH
#increment backfill days
$MYSQL -u$DB_USER -h $DB_HOST --password=$DB_PASSWORD $DB_NAME -e "${MYSQL_CMD}"
fi
@@ -87,20 +85,19 @@ elif [ "$THREADS" == "true" -a "$INNODB" != "true" ]; then
#make active groups current
if [[ $BINARIES == "true" ]] ; then
cd $MYISAM_PATH
cd $NEWZNAB_PATH
[ -f update_binaries_threaded.php ] && $PHP update_binaries_threaded.php
fi
#get backfill for all active groups
if [[ $BACKFILL == "true" ]] ; then
cd $MYISAM_PATH
cd $NEWZNAB_PATH
[ -f backfill_threaded.php ] && $PHP backfill_threaded.php
fi
wait
if [[ $BACKFILL == "true" ]] ; then
cd $INNODB_PATH
#increment backfill days
$MYSQL -u$DB_USER -h $DB_HOST --password=$DB_PASSWORD $DB_NAME -e "${MYSQL_CMD}"
fi
@@ -135,7 +132,6 @@ elif [ "$THREADS" != "true" -a "$INNODB" != "true" ]; then
wait
if [[ $BACKFILL == "true" ]] ; then
cd $INNODB_PATH
#increment backfill days
$MYSQL -u$DB_USER -h $DB_HOST --password=$DB_PASSWORD $DB_NAME -e "${MYSQL_CMD}"
fi
+10 -9
View File
@@ -7,6 +7,7 @@ export NEWZPATH="/var/www/newznab"
export NEWZNAB_PATH=$NEWZPATH"/misc/update_scripts"
export TESTING_PATH=$NEWZPATH"/misc/testing"
export ADMIN_PATH=$NEWZPATH"/www/admin"
export INNODB_PATH=$TESTING_PATH"/innodb"
export USERNAME="what is your name" # this is the user name that will run these scripts
export NEWZNAB_IMPORT_SLEEP_TIME="1" # in seconds - this includes import_nzb backfill and current fill, 0 may cause errors
export NEWZNAB_POST_SLEEP_TIME="1" # in seconds - this is for post processing - sleep between loops, 0 may cause errors
@@ -14,16 +15,16 @@ export MAXDAYS="210" #max days for backfill
export NZBS="/path/to/nzbs" #The path to the nzb dump you downloaded from torrents
#Choose to run the threaded or non-threaded newznab scripts true/false
export THREADS="true"
export THREADS="false"
#Choose your database, comment the one true/false
export INNODB="true"
export INNODB="false"
#Choose to run update_cleanup.php true/false
export CLEANUP="true"
export CLEANUP="false"
#Choose to run update_binaries true/false
export BINARIES="true"
export BINARIES="false"
#Choose to run backfill script true/false
export BACKFILL="true"
@@ -32,11 +33,11 @@ export BACKFILL="true"
export IMPORT="true"
#Select some monitoring script, if they are not installed, it will not affect the running of the scripts
export USE_HTOP="true"
export USE_NMON="true"
export USE_BWMNG="true"
export USE_IOTOP="true"
export USE_MYTOP="true"
export USE_HTOP="false"
export USE_NMON="false"
export USE_BWMNG="false"
export USE_IOTOP="false"
export USE_MYTOP="false"
#By using this script you understand that the programmer is not responsible for any loss of data, users, or sanity.
#You also agree that you were smart enough to make a backup of your database and files. Do you agree? yes/no
-4
View File
@@ -18,10 +18,6 @@ if [[ $AGREED == "no" ]]; then
exit
fi
export INNODB_PATH=$DIR"/bin/innodb"
export MYISAM_PATH=$DIR"/bin/myisam"
export START_PATH=$DIR
#delete some files
[ -f bin/lib/postprocess4.php ];rm bin/lib/postprocess4.php
[ -f bin/processAlternate4.php ];rm bin/processAlternate4.php