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>'; }
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, '', '&'); } // Result $template->items = $render->View(); $template->Display('layout_rss', 'text/xml'); }
// 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; }
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(); } } } }
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), '', '&') . '">' . 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'); }
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>).'); } }
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; }
/** * 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(); }
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; }
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(); }