diff --git a/core/_bootstrap.inc.php b/core/_bootstrap.inc.php index df92da94..e8d6559a 100644 --- a/core/_bootstrap.inc.php +++ b/core/_bootstrap.inc.php @@ -10,6 +10,7 @@ require_once "core/sys_config.inc.php"; require_once "core/util.inc.php"; require_once "lib/context.php"; require_once "vendor/autoload.php"; +require_once "core/imageboard.pack.php"; // set up and purify the environment _version_check(); diff --git a/core/block.class.php b/core/block.class.php index 1fbe0e3f..0fffb41f 100644 --- a/core/block.class.php +++ b/core/block.class.php @@ -44,6 +44,14 @@ class Block { */ public $id; + /** + * Should this block count as content for the sake of + * the 404 handler + * + * @var boolean + */ + public $is_content = true; + /** * Construct a block. * @@ -58,7 +66,11 @@ class Block { $this->body = $body; $this->section = $section; $this->position = $position; - $this->id = preg_replace('/[^\w]/', '',str_replace(' ', '_', is_null($id) ? (is_null($header) ? md5($body) : $header) . $section : $id)); + + if(is_null($id)) { + $id = (empty($header) ? md5($body) : $header) . $section; + } + $this->id = preg_replace('/[^\w]/', '',str_replace(' ', '_', $id)); } /** diff --git a/core/database.class.php b/core/database.class.php index 40575f33..ec1a7ce2 100644 --- a/core/database.class.php +++ b/core/database.class.php @@ -314,13 +314,8 @@ class MemcacheCache implements CacheEngine { */ public function __construct($args) { $hp = explode(":", $args); - if(class_exists("Memcache")) { - $this->memcache = new Memcache; - @$this->memcache->pconnect($hp[0], $hp[1]); - } - else { - print "no memcache"; exit; - } + $this->memcache = new Memcache; + @$this->memcache->pconnect($hp[0], $hp[1]); } /** @@ -378,6 +373,100 @@ class MemcacheCache implements CacheEngine { */ public function get_misses() {return $this->misses;} } +class MemcachedCache implements CacheEngine { + /** @var \Memcached|null */ + public $memcache=null; + /** @var int */ + private $hits=0; + /** @var int */ + private $misses=0; + + /** + * @param string $args + */ + public function __construct($args) { + $hp = explode(":", $args); + $this->memcache = new Memcached; + #$this->memcache->setOption(Memcached::OPT_COMPRESSION, False); + #$this->memcache->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); + #$this->memcache->setOption(Memcached::OPT_PREFIX_KEY, phpversion()); + $this->memcache->addServer($hp[0], $hp[1]); + } + + /** + * @param string $key + * @return array|bool|string + */ + public function get($key) { + assert('!is_null($key)'); + $key = urlencode($key); + + $val = $this->memcache->get($key); + $res = $this->memcache->getResultCode(); + + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + $hit = $res == Memcached::RES_SUCCESS ? "hit" : "miss"; + file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); + } + if($res == Memcached::RES_SUCCESS) { + $this->hits++; + return $val; + } + else if($res == Memcached::RES_NOTFOUND) { + $this->misses++; + return false; + } + else { + error_log("Memcached error during get($key): $res"); + } + } + + /** + * @param string $key + * @param mixed $val + * @param int $time + */ + public function set($key, $val, $time=0) { + assert('!is_null($key)'); + $key = urlencode($key); + + $this->memcache->set($key, $val, $time); + $res = $this->memcache->getResultCode(); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); + } + if($res != Memcached::RES_SUCCESS) { + error_log("Memcached error during set($key): $res"); + } + } + + /** + * @param string $key + */ + public function delete($key) { + assert('!is_null($key)'); + $key = urlencode($key); + + $this->memcache->delete($key); + $res = $this->memcache->getResultCode(); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); + } + if($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) { + error_log("Memcached error during delete($key): $res"); + } + } + + /** + * @return int + */ + public function get_hits() {return $this->hits;} + + /** + * @return int + */ + public function get_misses() {return $this->misses;} +} class APCCache implements CacheEngine { public $hits=0, $misses=0; @@ -466,10 +555,13 @@ class Database { private function connect_cache() { $matches = array(); - if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(memcache|apc)://(.*)#", CACHE_DSN, $matches)) { + if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(memcache|memcached|apc)://(.*)#", CACHE_DSN, $matches)) { if($matches[1] == "memcache") { $this->cache = new MemcacheCache($matches[2]); } + else if($matches[1] == "memcached") { + $this->cache = new MemcachedCache($matches[2]); + } else if($matches[1] == "apc") { $this->cache = new APCCache($matches[2]); } @@ -598,18 +690,15 @@ class Database { * @param string $sql */ private function count_execs($db, $sql, $inputarray) { - if ((defined('DEBUG_SQL') && DEBUG_SQL === true) || (!defined('DEBUG_SQL') && @$_GET['DEBUG_SQL'])) { - $fp = @fopen("data/sql.log", "a"); - if($fp) { - $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); - if(isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { - fwrite($fp, $sql." -- ".join(", ", $inputarray)."\n"); - } - else { - fwrite($fp, $sql."\n"); - } - fclose($fp); + if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { + $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); + if(isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { + $text = $sql." -- ".join(", ", $inputarray)."\n"; } + else { + $text = $sql."\n"; + } + file_put_contents("data/sql.log", $text, FILE_APPEND); } if(!is_array($inputarray)) $this->query_count++; # handle 2-dimensional input arrays @@ -617,6 +706,14 @@ class Database { else $this->query_count++; } + private function count_time($method, $start) { + if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { + $text = $method.":".(microtime(true) - $start)."\n"; + file_put_contents("data/sql.log", $text, FILE_APPEND); + } + $this->dbtime += microtime(true) - $start; + } + /** * Execute an SQL query and return an PDO result-set. * @@ -661,7 +758,7 @@ class Database { public function get_all($query, $args=array()) { $_start = microtime(true); $data = $this->execute($query, $args)->fetchAll(); - $this->dbtime += microtime(true) - $_start; + $this->count_time("get_all", $_start); return $data; } @@ -675,7 +772,7 @@ class Database { public function get_row($query, $args=array()) { $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); - $this->dbtime += microtime(true) - $_start; + $this->count_time("get_row", $_start); return $row ? $row : null; } @@ -693,7 +790,7 @@ class Database { foreach($stmt as $row) { $res[] = $row[0]; } - $this->dbtime += microtime(true) - $_start; + $this->count_time("get_col", $_start); return $res; } @@ -711,7 +808,7 @@ class Database { foreach($stmt as $row) { $res[$row[0]] = $row[1]; } - $this->dbtime += microtime(true) - $_start; + $this->count_time("get_pairs", $_start); return $res; } @@ -725,7 +822,7 @@ class Database { public function get_one($query, $args=array()) { $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); - $this->dbtime += microtime(true) - $_start; + $this->count_time("get_one", $_start); return $row[0]; } diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 755de669..0ee89a09 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -189,7 +189,7 @@ class Image { * @param string[] $tags * @return boolean */ - public function validate_accel($tags) { + public static function validate_accel($tags) { $yays = 0; $nays = 0; foreach($tags as $tag) { @@ -209,7 +209,7 @@ class Image { * @return null|PDOStatement * @throws SCoreException */ - public function get_accelerated_result($tags, $offset, $limit) { + public static function get_accelerated_result($tags, $offset, $limit) { global $database; if(!Image::validate_accel($tags)) { @@ -282,8 +282,7 @@ class Image { } else { $querylet = Image::build_search_querylet($tags); - $result = $database->execute($querylet->sql, $querylet->variables); - return $result->rowCount(); + return $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } } @@ -967,7 +966,7 @@ class Image { } } - assert('$positive_tag_id_array || $negative_tag_id_array'); + assert('$positive_tag_id_array || $negative_tag_id_array', @$_GET['q']); $wheres = array(); if (!empty($positive_tag_id_array)) { $positive_tag_id_list = join(', ', $positive_tag_id_array); @@ -1210,7 +1209,7 @@ function move_upload_to_archive(DataUploadEvent $event) { * Add a directory full of images * * @param $base string - * @return array + * @return array|string[] */ function add_dir($base) { $results = array(); @@ -1250,7 +1249,7 @@ function add_image($tmpname, $filename, $tags) { $metadata = array(); $metadata['filename'] = $pathinfo['basename']; $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = $tags; + $metadata['tags'] = Tag::explode($tags); $metadata['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); diff --git a/core/util.inc.php b/core/util.inc.php index 6b0e081c..f6a357ec 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -966,6 +966,17 @@ function data_path($filename) { return $filename; } +if (!function_exists('mb_strlen')) { + // TODO: we should warn the admin that they are missing multibyte support + function mb_strlen($str, $encoding) { + return strlen($str); + } + function mb_internal_encoding($encoding) {} + function mb_strtolower($str) { + return strtolower($str); + } +} + /** * @param string $url * @param string $mfile diff --git a/ext/blocks/main.php b/ext/blocks/main.php index 43043370..d1f0f6cf 100644 --- a/ext/blocks/main.php +++ b/ext/blocks/main.php @@ -42,7 +42,9 @@ class Blocks extends Extension { foreach($blocks as $block) { $path = implode("/", $event->args); if(strlen($path) < 4000 && fnmatch($block['pages'], $path)) { - $page->add_block(new Block($block['title'], $block['content'], $block['area'], $block['priority'])); + $b = new Block($block['title'], $block['content'], $block['area'], $block['priority']); + $b->is_content = false; + $page->add_block($b); } } diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index 34645e91..a5b999e7 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -73,7 +73,7 @@ class BulkAddCSV extends Extension { $metadata = array(); $metadata['filename'] = $pathinfo['basename']; $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = $tags; + $metadata['tags'] = Tag::explode($tags); $metadata['source'] = $source; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); diff --git a/ext/chatbox/main.php b/ext/chatbox/main.php index 1ac474c4..80081d46 100644 --- a/ext/chatbox/main.php +++ b/ext/chatbox/main.php @@ -30,6 +30,7 @@ class Chatbox extends Extension { // loads the chatbox at the set location $html = "
"; $chatblock = new Block("Chatbox", $html, "main", 97); + $chatblock->is_content = false; $page->add_block($chatblock); } } diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 182a1848..a9b05650 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -304,9 +304,14 @@ class CronUploader extends Extension { /** * Generate the necessary DataUploadEvent for a given image and tags. + * + * @param string $tmpname + * @param string $filename + * @param string $tags */ private function add_image($tmpname, $filename, $tags) { assert ( file_exists ( $tmpname ) ); + assert('is_string($tags)'); $pathinfo = pathinfo ( $filename ); if (! array_key_exists ( 'extension', $pathinfo )) { @@ -315,7 +320,7 @@ class CronUploader extends Extension { $metadata = array(); $metadata ['filename'] = $pathinfo ['basename']; $metadata ['extension'] = $pathinfo ['extension']; - $metadata ['tags'] = ""; // = $tags; doesn't work when not logged in here + $metadata ['tags'] = array(); // = $tags; doesn't work when not logged in here $metadata ['source'] = null; $event = new DataUploadEvent ( $tmpname, $metadata ); send_event ( $event ); @@ -329,7 +334,7 @@ class CronUploader extends Extension { // Set tags $img = Image::by_id($event->image_id); - $img->set_tags($tags); + $img->set_tags(Tag::explode($tags)); } private function generate_image_queue($base = "", $subdir = "") { diff --git a/ext/handle_404/main.php b/ext/handle_404/main.php index b7996848..a17e80f7 100644 --- a/ext/handle_404/main.php +++ b/ext/handle_404/main.php @@ -43,10 +43,7 @@ class Handle404 extends Extension { private function count_main($blocks) { $n = 0; foreach($blocks as $block) { - if($block->section == "main") $n++; // more hax. - } - if(ext_is_live("Chatbox")) { - $n--; // even more hax. + if($block->section == "main" && $block->is_content) $n++; // more hax. } return $n; } diff --git a/ext/handle_archive/main.php b/ext/handle_archive/main.php index 1fe56ca6..ad43c4ac 100644 --- a/ext/handle_archive/main.php +++ b/ext/handle_archive/main.php @@ -35,8 +35,10 @@ class ArchiveFileHandler extends Extension { exec($cmd); $results = add_dir($tmpdir); if(count($results) > 0) { - // FIXME no theme? - $this->theme->add_status("Adding files", $results); + // Not all themes have the add_status() method, so need to check before calling. + if (method_exists($this->theme, "add_status")) { + $this->theme->add_status("Adding files", $results); + } } deltree($tmpdir); $event->image_id = -2; // default -1 = upload wasn't handled diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 6122cb46..719648d4 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -37,7 +37,7 @@ class FlashFileHandler extends DataHandlerExtension { $image->hash = $metadata['hash']; $image->filename = $metadata['filename']; $image->ext = $metadata['extension']; - $image->tag_array = $metadata['tags']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); $image->source = $metadata['source']; $info = getimagesize($filename); diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index beda9596..31d5e337 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -67,7 +67,7 @@ class IcoFileHandler extends Extension { $image->hash = $metadata['hash']; $image->filename = $metadata['filename']; $image->ext = $metadata['extension']; - $image->tag_array = $metadata['tags']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); $image->source = $metadata['source']; return $image; diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 9bbbe55a..4f0eae9e 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -43,7 +43,7 @@ class MP3FileHandler extends DataHandlerExtension { $image->filename = $metadata['filename']; $image->ext = $metadata['extension']; - $image->tag_array = $metadata['tags']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); $image->source = $metadata['source']; return $image; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 38ba6b3d..149677eb 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -35,7 +35,7 @@ class PixelFileHandler extends DataHandlerExtension { $image->hash = $metadata['hash']; $image->filename = (($pos = strpos($metadata['filename'],'?')) !== false) ? substr($metadata['filename'],0,$pos) : $metadata['filename']; $image->ext = (($pos = strpos($metadata['extension'],'?')) !== false) ? substr($metadata['extension'],0,$pos) : $metadata['extension']; - $image->tag_array = $metadata['tags']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); $image->source = $metadata['source']; return $image; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index c932f6ba..2e58dbd3 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -75,7 +75,7 @@ class SVGFileHandler extends Extension { $image->hash = $metadata['hash']; $image->filename = $metadata['filename']; $image->ext = $metadata['extension']; - $image->tag_array = $metadata['tags']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); $image->source = $metadata['source']; return $image; diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 4edbcc32..a31ac781 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -170,7 +170,7 @@ class VideoFileHandler extends DataHandlerExtension { $image->filesize = $metadata['size']; $image->hash = $metadata['hash']; $image->filename = $metadata['filename']; - $image->tag_array = $metadata['tags']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); $image->source = $metadata['source']; return $image; diff --git a/ext/handle_video/theme.php b/ext/handle_video/theme.php index 9cc28fa5..21d4ac61 100644 --- a/ext/handle_video/theme.php +++ b/ext/handle_video/theme.php @@ -9,8 +9,9 @@ class VideoFileHandlerTheme extends Themelet { $full_url = make_http($ilink); $autoplay = $config->get_bool("video_playback_autoplay"); $loop = $config->get_bool("video_playback_loop"); + $player = make_link('lib/vendor/swf/flashmediaelement.swf'); - $html = "Video not playing? Click here to download the file.