Example #1
0
 public function Run($id = null)
 {
     echo $this->Title('NZB');
     $count = 0;
     $query = 'SELECT "postcat"."postid","posts"."subject" FROM "postcat" LEFT JOIN "posts" ON ("posts"."id" = "postcat"."postid") ' . 'WHERE ("postcat"."nzb_date" < "postcat"."updated") ORDER BY "postcat"."post_date" DESC ';
     $rs = static::$conn->Execute($query);
     while ($row = $rs->Fetch()) {
         echo $row['postid'] . ' :: ' . SafeHTML($row['subject']) . '<br />';
         // Create parent folder if it doesn't exist yet
         if (!file_exists(dirname(Post::NZBFile($row['postid'])))) {
             mkdir(dirname(Post::NZBFile($row['postid'])), 0755, true);
         }
         // Write NZB file
         file_put_contents(Post::NZBFile($row['postid']), Post::NZB($row['postid']));
         // Update DB
         if (file_exists(Post::NZBFile($row['postid']))) {
             static::$conn->AutoUpdate('postcat', array('nzb_date' => time()), 'postid = ?', $row['postid']);
         }
         $count++;
         // Abort if processing if to much NZB files to process
         if (GetMicroTime() - $this->time['start'] > self::TIME_LIMIT) {
             break;
         }
     }
     echo '<p><b>' . $count . '</b> NZB files processed.</p>';
 }
Example #2
0
 public function ActionDefault()
 {
     $template = new Template();
     $render = new RSSRender(static::$config['url']['rss']);
     // Title
     if (isset($_REQUEST['q']) && !empty($_REQUEST['q'])) {
         $template->title = SafeHTML(trim($_REQUEST['q']));
     } elseif (isset($_REQUEST['cat']) && !empty($_REQUEST['cat'])) {
         $cat = trim($_REQUEST['cat']);
         $template->title = SafeHTML($cat != 'dvd' ? ucfirst($cat) : strtoupper($cat));
     } else {
         $template->title = 'All';
     }
     $template->title .= ' :: ';
     // RSS
     $template->rss = 'http://' . static::$config['url']['domain'] . static::$config['url']['rss'];
     if (count($render->link) > 0) {
         $template->rss .= '?' . http_build_query($render->link, '', '&amp;');
     }
     // Result
     $template->items = $render->View();
     $template->Display('layout_rss', 'text/xml');
 }
Example #3
0
    // Abort if load exceeds maximum threshold
    #$loadavg = LoadAverage();
    #if (isset($config['option']['cron']['loadlimit']) && ($loadavg !== false) && isset($loadavg[1]) && ($loadavg[1] > $config['option']['cron']['loadlimit']))
    #	die('<b>Task aborted</b>: load average '.$loadavg[1].' exceeds threshold');
    // Get parameters
    if (isset($_SERVER['argc']) && isset($_SERVER['argv']) && $_SERVER['argc'] >= 2) {
        $object = isset($_SERVER['argv'][1]) ? strtolower($_SERVER['argv'][1]) : '';
        $id = isset($_SERVER['argv'][2]) ? intval($_SERVER['argv'][2]) : null;
    } elseif (isset($_REQUEST['worker'])) {
        $object = strtolower($_REQUEST['worker']);
        $id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
    } else {
        throw new Exception('No worker specified');
    }
    // Load worker class
    if (strpos($object, '.') !== false) {
        throw new Exception('Invalid worker name: ' . SafeHTML($object));
    }
    if (file_exists($config['path']['lib'] . '/worker.' . $object . '.php')) {
        require_once $config['path']['lib'] . '/worker.' . $object . '.php';
    }
    if (!class_exists($object)) {
        throw new Exception('Unknown worker: ' . SafeHTML($object));
    }
    // Execute
    eval('$worker = new ' . $object . '();');
    $worker->Run($id);
    unset($worker);
} catch (Exception $e) {
    echo $e->getMessage();
}
 /**
  * Finds object in database and returns (an array of) objects with values.
  */
 public static function Find($where = null, $query = null)
 {
     static::Init();
     // Build query if no custom query specified
     if (is_null($query)) {
         $query = 'SELECT * FROM "' . static::$table . '"';
         // $where is an array with key/value pairs
         if (is_array($where) && count($where) > 0) {
             $fields = array();
             foreach ($where as $key => $value) {
                 $fields[] = static::$conn->WhereField($key, static::$columns[$key]['type'] == static::TYPE_TEXT);
             }
             $query .= ' WHERE (' . implode(') AND (', $fields) . ') ';
             $args = array_values($where);
         } elseif (!is_null($where)) {
             $query .= ' WHERE ' . static::KeyStatic();
             $args = $where;
         }
     } else {
         // Get arguments (for prepared statements)
         if (func_num_args() > 2) {
             $args = func_get_args();
             if (isset($args[2]) && is_array($args[2])) {
                 $args = array_values($args[2]);
             } else {
                 $args = array_slice($args, 2);
             }
             // Use all arguments as values (except for where and query of course)
         }
     }
     if (isset($args)) {
         $rs = static::$conn->Execute($query, $args);
     } else {
         $rs = static::$conn->Execute($query);
     }
     // No rows = raise error
     if ($rs->num_rows == 0) {
         throw new ActiveRecord_NotFoundException('Row not found in table <i>' . SafeHTML(static::$table) . '</i>');
     }
     // One row = return single instance
     if (!is_null($where) && $rs->num_rows == 1) {
         $row = $rs->Fetch();
         $instance = new static();
         $instance->Load($row);
         return $instance;
     }
     // Else: return array of instances
     $result = array();
     while ($row = $rs->Fetch()) {
         $instance = new static();
         $instance->Load($row);
         $result[] = $instance;
     }
     // Do not return array for single result
     if (is_array($result) && count($result) == 1) {
         $result = reset($result);
     }
     return $result;
 }
Example #5
0
 public function ActionDefault()
 {
     // If a _lot_ of NZB files are requested together, we might run out of memory
     ini_set('memory_limit', '1024M');
     $ids = array();
     // Get post
     if (isset($_POST['id']) && is_array($_POST['id'])) {
         foreach ($_POST['id'] as $id) {
             $id = intval($id);
             if ($id > 0) {
                 $ids[$id] = $id;
             }
         }
     } elseif (isset($this->params[0]) && (int) $this->params[0] > 0) {
         $ids[(int) $this->params[0]] = (int) $this->params[0];
     } else {
         die('<b>Error</b>: invalid post specified.');
     }
     $nzb = '';
     $template = new Template();
     $template->body = '';
     foreach ($ids as $id) {
         try {
             $post = Post::FindByID($id);
         } catch (ActiveRecord_NotFoundException $e) {
             die('<b>Error</b>: post ' . SafeHTML($id) . ' does not exist.');
         }
         // Updated time must be at least a minute ago, otherwise NZB cache may not be updated
         if (file_exists(Post::NZBFile($post->id)) && $post->updated < time() - 60) {
             $template->body .= file_get_contents(Post::NZBFile($post->id));
         } else {
             $template->body .= Post::NZB($post->id);
         }
         // NZB name is taken from first post in list
         if (empty($nzb)) {
             $nzb = Post::NZBName($post->subject);
         }
         // Don't try to download all BDMV raws in one go please
         if (memory_get_peak_usage() > self::MEMORY_LIMIT) {
             die('<b>Error:</b> out of memory. Please download fewer NZB files together.');
         }
     }
     // If multiple posts in one NZB, remove numbers from NZB name
     if (count($ids) > 1) {
         $nzb = preg_replace('/\\s+\\d+\\.nzb$/i', '.nzb', $nzb);
         $nzb = preg_replace('/\\s+\\d+\\s+/i', ' ', $nzb);
         $nzb = preg_replace('/\\s+/i', ' ', $nzb);
     }
     header('Content-disposition: inline; filename="' . $nzb . '"');
     // Output NZB file
     $template->Display('layout_nzb', 'application/x-nzb', false);
     // Count download(s)
     if (isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR'])) {
         // We don't store actual IP, only MD5 of the IP (for privacy)
         $ip = md5(strtolower(trim($_SERVER['REMOTE_ADDR'])));
         foreach ($ids as $id) {
             try {
                 $download = Download::Find(array('postid' => $id, 'userip' => $ip));
                 // Found = ignore (count downloads only once)
             } catch (ActiveRecord_NotFoundException $e) {
                 // Not found = add
                 $download = new Download();
                 $download->postid = $id;
                 $download->userip = $ip;
                 $download->Save();
             }
         }
     }
 }
Example #6
0
 protected function Details($id)
 {
     // Details screen may use a lot of memory if there are a lot of files in a post
     ini_set('memory_limit', '512M');
     $template = new Template();
     $result = '';
     // Get post
     try {
         $post = Post::FindByID($id);
         $author = Author::FindByID($post->authorid);
     } catch (ActiveRecord_NotFoundException $e) {
         die('<b>Error</b>: post ' . SafeHTML($this->params[0]) . ' does not exist.');
     }
     // Cache newsgroups
     try {
         $newsgroups = array();
         $groups = Newsgroup::FindAll();
         if (!is_array($groups)) {
             $groups = array($groups);
         }
         foreach ($groups as $group) {
             $newsgroups[$group->id] = $group->name;
         }
     } catch (ActiveRecord_NotFoundException $e) {
         die('<b>Error</b>: database problem - no newsgroups defined.');
     }
     $result .= '<table cellspacing="0"><tr><th>Poster</th><th>Newsgroups</th></tr>' . "\n";
     $poster = trim(preg_replace('/\\s+/', ' ', str_replace(array('@', '(', ')', '[', ']', ',', '.', '!', '-', '#', '^', '$', '+', '/', '\\'), ' ', $author->name)));
     $result .= '<tr><td class="split"><a href="' . static::$config['url']['base'] . '?' . http_build_query(array('q' => '@poster ' . $poster), '', '&amp;') . '">' . SafeHTML($author->name) . '</a></td><td class="split">';
     try {
         $list = array();
         $groups = PostGroup::FindByPostID($post->id);
         if (!is_array($groups)) {
             $list[] = $newsgroups[$groups->groupid];
         } else {
             foreach ($groups as $group) {
                 $list[] = $newsgroups[$group->groupid];
             }
         }
         $result .= implode('<br />', $list);
     } catch (ActiveRecord_NotFoundException $e) {
         die('<b>Error</b>: database problem - post is not associated with any newsgroup.');
     }
     $result .= '</tr>' . "\n" . '</table><br />' . "\n";
     // Get files
     try {
         $result .= '<table cellspacing="0">' . "\n";
         $result .= '<tr><th>Date</th><th>Subject</th><th>Parts</th><th>Size</th></tr>' . "\n";
         $articles = Article::Find(null, 'SELECT * FROM `articles` WHERE `postid` = ' . $post->id . ' ORDER BY `subject` ASC');
         if (!is_array($articles)) {
             $articles = array($articles);
         }
         foreach ($articles as $article) {
             $result .= '<tr>' . "\n";
             $result .= '<td class="date">' . gmdate('Y-m-d H:i', $article->post_date) . '</td>' . "\n";
             $result .= '<td class="subject">' . SafeHTML($article->subject) . '</td>';
             if ($article->parts_found != $article->parts_total) {
                 $result .= '<td class="parts"><span class="warning">' . SafeHTML($article->parts_found) . ' / ' . SafeHTML($article->parts_total) . '</span></td>';
             } else {
                 $result .= '<td class="parts">' . SafeHTML($article->parts_found) . ' / ' . SafeHTML($article->parts_total) . '</span></td>';
             }
             $result .= '<td class="size">' . FormatSize($article->size, 2) . '</td>';
             $result .= '</tr>' . "\n";
         }
         $result .= '</table>' . "\n";
     } catch (ActiveRecord_NotFoundException $e) {
         die('<b>Error</b>: database problem - post has no associated articles.');
     }
     $template->body = $result;
     $template->Display('layout_ajax');
 }
Example #7
0
 protected function FloodLimit()
 {
     $class = get_called_class();
     $query = 'SELECT COUNT(*) AS total FROM "' . $class::$table . '" WHERE ("registered" > (UNIX_TIMESTAMP()-86400)) AND ("ip" = ?)';
     $rs = static::$conn->Execute($query, $this->ip);
     $row = $rs->Fetch();
     if ($row['total'] >= self::FLOOD_LIMIT) {
         throw new ErrorException('Flood limit reached: in the past 24 hours <b>' . SafeHTML($row['total']) . '</b> accounts have been registered from your IP (<b>' . SafeHTML($this->ip) . '</b>).');
     }
 }
Example #8
0
 public function View()
 {
     $result = '';
     $rs = static::$conn->Execute($this->Query(), $this->values);
     while ($row = $rs->Fetch()) {
         $template = new Template('rss_item');
         $template->title = Post::FilenameFilter($row['subject']);
         $template->link = 'http://' . static::$config['url']['domain'] . static::$config['url']['nzb'] . '/' . $row['id'];
         $template->vanity = '/' . rawurlencode(Post::NZBName($row['subject']));
         $template->desc = '<i>Age</i>: ' . floor((time() - $row['post_date'] + 1) / 86400) . ' days<br />';
         $template->desc .= '<i>Size</i>: ' . FormatSize($row['size'], 2) . '<br />';
         if ($row['parts_found'] == $row['parts_total']) {
             $template->desc .= '<i>Parts</i>: 100%<br />';
         } elseif ($row['parts_total'] > 0) {
             $template->desc .= '<i>Parts</i>: ' . number_format((int) $row['parts_found'] / (int) $row['parts_total'] * 100, 2) . '%<br />';
         } else {
             $template->desc .= '<i>Parts</i>: 0%<br />';
         }
         if (!empty($row['stats'])) {
             parse_str($row['stats'], $files);
             if (count($files) > 0) {
                 $list = array();
                 foreach ($files as $type => $count) {
                     $list[] = $count . ' ' . SafeHTML($type);
                 }
                 $template->desc .= '<i>Files</i>: ' . implode(', ', $list) . '<br />';
             }
         }
         $template->desc .= '<i>Subject</i>: ' . SafeHTML($row['subject']);
         $template->cat = 'other';
         if (isset($GLOBALS['catname'][$row['catid']])) {
             $template->cat = strtolower($GLOBALS['catname'][$row['catid']]);
         }
         $template->cat = $template->cat != 'dvd' ? ucfirst($template->cat) : strtoupper($template->cat);
         $template->size = SafeHTML($row['size']);
         $template->date = gmdate('r', $row['post_date']);
         $result .= $template . "\n";
     }
     return $result;
 }
Example #9
0
 /**
  * Retrieves template from disc (or cache) and returns output of Parse()
  */
 public function Fetch($template = null)
 {
     if (!is_null($template)) {
         $this->_template = $template;
     }
     if (!isset(self::$_cache[$this->_template])) {
         if (is_null($this->_template)) {
             throw new ErrorException('No template specified');
         }
         if (strpos($this->_template, '/') !== false) {
             throw new ErrorException('Template <i>' . SafeHTML($this->_template) . '</i> is invalid');
         }
         if (!isset($GLOBALS['config']['path']['template'])) {
             throw new ErrorException('Template path not specified');
         }
         $file = $GLOBALS['config']['path']['template'] . '/' . $this->_template . '.tpl';
         if (!file_exists($file)) {
             throw new ErrorException('Template <i>' . $file . '</i> not found');
         }
         self::$_cache[$this->_template] = file_get_contents($file);
     }
     return $this->Parse();
 }
Example #10
0
 public static function NZB($postid)
 {
     static::Init();
     $result = '';
     // Get newsgroups
     $groups = '';
     $rs = static::$conn->Execute('SELECT "groupid" FROM "postgroup" WHERE "postid" = ? ', $postid);
     while ($row = $rs->Fetch()) {
         $groups .= "\t\t\t<group>" . Newsgroup::Lookup($row['groupid']) . "</group>\n";
     }
     // Get files
     $rs = static::$conn->Execute('SELECT "subject","authorid","parts","post_date" FROM "articles" WHERE "postid" = ? ', $postid);
     while ($row = $rs->Fetch()) {
         if (strlen($row['parts']) == 0) {
             continue;
         }
         // Empty parts value = SKIP file
         $parts = unserialize(gzinflate($row['parts']));
         if (isset($parts['id']) && isset($parts['size'])) {
             $result .= "\t" . '<file poster="' . SafeHTML(Author::Lookup($row['authorid'])) . '" date="' . $row['post_date'] . '" subject="' . SafeHTML($row['subject']) . '">' . "\n";
             // Groups
             $result .= "\t\t<groups>\n" . $groups . "\t\t</groups>\n";
             // Segments
             $result .= "\t\t<segments>\n";
             ksort($parts['size']);
             foreach ($parts['size'] as $key => $value) {
                 if (isset($parts['id'][$key])) {
                     $result .= "\t\t\t" . '<segment bytes="' . $value . '" number="' . $key . '">' . SafeHTML($parts['id'][$key]) . "</segment>\n";
                 }
             }
             $result .= "\t\t</segments>\n";
             $result .= "\t</file>\n";
         }
     }
     return $result;
 }
Example #11
0
 public function Run()
 {
     echo $this->Title('Posts');
     $total_new = 0;
     $total_found = 0;
     $total_ignored = 0;
     // Lets not bother with spammers
     $skipauthors = array();
     $query = 'SELECT "authorid",COUNT(*) AS "total",SUM("hidden") AS "hidden" FROM "posts" GROUP BY "authorid" ';
     $rs = static::$conn->Execute($query);
     while ($row = $rs->Fetch()) {
         // Total posts equals hidden posts
         if ($row['total'] == $row['hidden'] && $row['hidden'] > self::SPAM_POST_THRESHOLD) {
             $skipauthors[$row['authorid']] = $row['authorid'];
         }
     }
     // Process new articles by author - each author gets 30 seconds so that spam can't hold up useful posts
     $authors = array();
     $query = 'SELECT "articles"."authorid","authors"."name" FROM "articles" ' . 'LEFT JOIN "authors" ON ("authors"."id" = "articles"."authorid") ' . 'WHERE ("articles"."postid" = 0) AND ("articles"."created" > ?) ' . (count($skipauthors) > 0 ? 'AND ("articles"."authorid" NOT IN (' . implode(',', $skipauthors) . ')) ' : '') . 'GROUP BY "articles"."authorid" ' . 'ORDER BY RAND() ';
     // Ensures that if a single author still holds up script too long, next time luck might change
     $rs = static::$conn->Execute($query, time() - self::MATCH_RECENT);
     while ($row = $rs->Fetch()) {
         $authors[$row['authorid']] = $row['name'];
     }
     foreach ($authors as $author_id => $author_name) {
         try {
             // Begin transaction
             static::$conn->BeginTransaction();
             $start = GetMicroTime();
             echo '<p><i>Processing articles posted by <b>' . SafeHTML($author_name) . '</b></i><br />';
             $count = 0;
             $new = 0;
             $found = 0;
             $ignored = 0;
             $skip = array();
             while (GetMicroTime() - $start < self::TIME_LIMIT) {
                 // Get article not yet associated with a post
                 $query = 'SELECT "id","authorid","subject","post_date" FROM "articles" ' . 'WHERE ("postid" = 0) AND ("authorid" = ?) AND ("created" > ?) ' . (count($skip) > 0 ? 'AND ("id" > ' . intval(end($skip)) . ') ' : '') . 'ORDER BY "id" ASC LIMIT 0,1';
                 $rs = static::$conn->Execute($query, $author_id, time() - self::MATCH_RECENT);
                 $row = $rs->Fetch();
                 // Abort if no more articles
                 if (!$row) {
                     break;
                 }
                 // Reset
                 $this->sql = $row['subject'];
                 $this->sql_alt = $row['subject'];
                 $this->parts = null;
                 $this->pos = null;
                 // Create Queries
                 $this->CreateQueries();
                 echo SafeHTML($row['subject']) . ' - ';
                 $strategy = $this->DetermineStrategy($row);
                 // If first pass failed, try a second time
                 if ($strategy == self::STRATEGY_IGNORE) {
                     $strategy = $this->SecondPass($row);
                 }
                 // Skip ignored
                 if ($strategy == self::STRATEGY_IGNORE) {
                     $ignored++;
                     $skip[$row['id']] = $row['id'];
                     echo ' - <i><b style="color:#990000">Ignored</b></i><br />';
                     continue;
                 }
                 // Find existing post (if any)
                 $query = 'SELECT "postid" FROM "articles" WHERE ("postid" != 0) AND ("subject" LIKE ?) AND ("authorid" = ?) AND ("post_date" > ?) AND ("post_date" < ?) LIMIT 0,1';
                 $rs = static::$conn->Execute($query, $strategy == self::STRATEGY_PRIMARY ? $this->sql : $this->sql_alt, $row['authorid'], $row['post_date'] - self::MATCH_RANGE, $row['post_date'] + self::MATCH_RANGE);
                 $match = $rs->Fetch();
                 if ($match) {
                     $found++;
                     $post = Post::FindByID($match['postid']);
                     echo ' - <i><b style="color:#000099">Found</b></i><br />';
                 } else {
                     $new++;
                     $post = new Post();
                     // We need the post ID so save first
                     $post->Save();
                     echo ' - <i><b style="color:#009900">New</b></i><br />';
                 }
                 // Update articles
                 $query = 'UPDATE "articles" SET "postid" = ?  WHERE ("postid" = 0) AND ("subject" LIKE ?) AND ("authorid" = ?) AND ("post_date" > ?) AND ("post_date" < ?)';
                 static::$conn->Execute($query, $post->id, $strategy == self::STRATEGY_PRIMARY ? $this->sql : $this->sql_alt, $row['authorid'], $row['post_date'] - self::MATCH_RANGE, $row['post_date'] + self::MATCH_RANGE);
                 // Queue post recalculation + post group update (initializes all missing fields for new posts too)
                 Post::Queue($post->id);
                 // Sleep for 0.1 seconds to give database time to process
                 usleep(100000);
                 if ($count > self::ARTICLE_LIMIT) {
                     break;
                 }
                 $count++;
             }
             // Update all posts in one go (in case one post was updated more than once)
             $posts = Post::Update();
             // Commit transaction
             static::$conn->Commit();
         } catch (Exception $e) {
             // Rollback on error
             static::$conn->Rollback();
             throw $e;
         }
         echo '<b>' . $new . '</b> posts added, <b>' . $found . '</b> posts updated, <b>' . $ignored . '</b> posts ignored (<b>' . number_format(GetMicroTime() - $start, 2) . '</b> seconds)</p>';
         $total_new += $new;
         $total_found += $found;
         $total_ignored += $ignored;
         // Abort if processing all authors takes to much time
         if (GetMicroTime() - $this->time['start'] > self::MAX_TIME_LIMIT) {
             break;
         }
     }
     echo '<p><i>Total <b>' . $total_new . '</b> posts added, <b>' . $total_found . '</b> posts updated, <b>' . $total_ignored . '</b> posts ignored</i></p>';
     // Detect spam
     $this->DetectSpam();
     // Update last 1000 number of posts that are marked hidden yet are still being listed (=BAD!)
     $query = 'SELECT "id" FROM "posts" LEFT JOIN "postcat" ON ("postcat"."postid" = "posts"."id") ' . 'WHERE ("postcat"."postid" IS NOT NULL) ' . 'AND ("posts"."hidden" = 1) ' . 'ORDER BY "posts"."id" DESC ' . 'LIMIT 0,1000 ';
     $rs = static::$conn->Execute($query);
     while ($row = $rs->Fetch()) {
         $post = Post::Find($row['id']);
         $post->Save();
     }
     // Update last 100 number of posts that are not being listed even though they're not marked hidden (ie: they were still incomplete when last time encountered)
     $query = 'SELECT "id" FROM "posts" LEFT JOIN "postcat" ON ("postcat"."postid" = "posts"."id") ' . 'WHERE ("postcat"."postid" IS NULL) ' . 'AND ("posts"."hidden" = 0) ' . 'AND ("posts"."post_date" < ' . (time() - 600) . ') ' . 'AND ("posts"."files" > 1) ' . 'AND ("posts"."files" < ' . self::MAX_POST_FILES_LIMIT . ') ' . 'AND ("posts"."size" >= 1048576) ' . 'ORDER BY "posts"."post_date" DESC ' . 'LIMIT 0,100 ';
     $rs = static::$conn->Execute($query);
     while ($row = $rs->Fetch()) {
         $post = Post::Find($row['id']);
         $post->Save();
     }
     // Update last 100 number of posts that have been last updated longest time ago
     $rs = static::$conn->Execute('SELECT "id" FROM "posts" ORDER BY "updated" ASC LIMIT 0,100 ');
     while ($row = $rs->Fetch()) {
         $post = Post::Find($row['id']);
         $post->Save();
     }
     PostCat::Update();
 }