str_replace('%s', '(\w{1,8})', preg_quote($config['board_path'], '/')), 'page' => str_replace('%d', '(\d+)', preg_quote($config['file_page'], '/')), 'img' => preg_quote($config['dir']['img'], '/'), 'thumb' => preg_quote($config['dir']['thumb'], '/'), 'res' => preg_quote($config['dir']['res'], '/'), 'index' => preg_quote($config['file_index'], '/') ); if(preg_match('/^\/?$/', $query)) { // Dashboard $fieldset = array( 'Boards' => '', 'Noticeboard' => '', 'Administration' => '', 'Themes' => '', 'Search' => '', 'Update' => '', 'Logout' => '' ); // Boards $fieldset['Boards'] .= ulBoards(); if(hasPermission($config['mod']['noticeboard'])) { if(!$config['cache']['enabled'] || !($fieldset['Noticeboard'] = cache::get('noticeboard_preview'))) { $query = prepare("SELECT `noticeboard`.*, `username` FROM `noticeboard` LEFT JOIN `mods` ON `mods`.`id` = `mod` ORDER BY `id` DESC LIMIT :limit"); $query->bindValue(':limit', $config['mod']['noticeboard_dashboard'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); $fieldset['Noticeboard'] .= '
  • '; $_body = ''; while($notice = $query->fetch()) { $_body .= '
  • ' . ($notice['subject'] ? $notice['subject'] : '' . _('no subject') . '' ) . ' — by ' . (isset($notice['username']) ? utf8tohtml($notice['username']) : '???') . ' at ' . strftime($config['post_date'], $notice['time']) . '
  • '; } if(!empty($_body)) { $fieldset['Noticeboard'] .= '
  • '; } if($config['cache']['enabled']) cache::set('noticeboard_preview', $fieldset['Noticeboard']); } $fieldset['Noticeboard'] .= '' . _('View all entries') . '
  • '; $query = prepare("SELECT COUNT(*) AS `count` FROM `pms` WHERE `to` = :id AND `unread` = 1"); $query->bindValue(':id', $mod['id']); $query->execute() or error(db_error($query)); $count = $query->fetch(); $count = $count['count']; $fieldset['Noticeboard'] .= '
  • ' . _('PM Inbox') . ($count > 0 ? ' (' . $count . ' unread)' : '') . '
  • '; $fieldset['Noticeboard'] .= '
  • ' . _('News') . '
  • '; } if(hasPermission($config['mod']['reports'])) { $fieldset['Administration'] .= '
  • ' . _('Report queue') . '
  • '; } if(hasPermission($config['mod']['view_banlist'])) { $fieldset['Administration'] .= '
  • ' . _('Ban list') . '
  • '; } if(hasPermission($config['mod']['manageusers'])) { $fieldset['Administration'] .= '
  • ' . _('Manage users') . '
  • '; } elseif(hasPermission($config['mod']['change_password'])) { $fieldset['Administration'] .= '
  • ' . _('Change own password') . '
  • '; } if(hasPermission($config['mod']['modlog'])) { $fieldset['Administration'] .= '
  • ' . _('Moderation log') . '
  • '; } if(hasPermission($config['mod']['rebuild'])) { $fieldset['Administration'] .= '
  • ' . _('Rebuild static files') . '
  • '; } if(hasPermission($config['mod']['rebuild']) && $config['cache']['enabled']) { $fieldset['Administration'] .= '
  • ' . _('Clear cache') . '
  • '; } if(hasPermission($config['mod']['show_config'])) { $fieldset['Administration'] .= '
  • ' . _('Show configuration') . '
  • '; } if(hasPermission($config['mod']['themes'])) { $fieldset['Themes'] .= '
  • ' . _('Manage themes') . '
  • '; } if(hasPermission($config['mod']['search'])) { $fieldset['Search'] .= '
  • ' . ' ' . '' . '' . '
    ' . '

    ' . _('(Search is case-insensitive, and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.)') . '

    ' . '
  • '; } if($mod['type'] >= ADMIN && $config['check_updates']) { if(!$config['version']) error(_('Could not find current version! (Check .installed)')); if(isset($_COOKIE['update'])) { $latest = unserialize($_COOKIE['update']); } else { $ctx = stream_context_create(array( 'http' => array( 'timeout' => 3 ) ) ); if($code = @file_get_contents('http://tinyboard.org/version.txt', 0, $ctx)) { eval($code); if(preg_match('/v(\d+)\.(\d)\.(\d+)(-dev.+)?$/', $config['version'], $m)) { $current = array( 'massive' => (int)$m[1], 'major' => (int)$m[2], 'minor' => (int)$m[3] ); if(isset($m[4])) { // Development versions are always ahead in the versioning numbers $current['minor'] --; } } // Check if it's newer if( $latest['massive'] > $current['massive'] || $latest['major'] > $current['major'] || ($latest['massive'] == $current['massive'] && $latest['major'] == $current['major'] && $latest['minor'] > $current['minor'] )) { $latest = $latest; } else $latest = false; } else { // Couldn't get latest version // TODO: Display some sort of warning message $latest = false; } setcookie('update', serialize($latest), time() + $config['check_updates_time'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, false, true); } if($latest) { $fieldset['Update'] .= '
  • A newer version of Tinyboard (v' . $latest['massive'] . '.' . $latest['major'] . '.' . $latest['minor'] . ') is available! See http://tinyboard.org/ for upgrade instructions.
  • '; } } $fieldset['Logout'] .= '
  • ' . _('Logout') . '
  • '; // TODO: Statistics, etc, in the dashboard. $body = ''; foreach($fieldset as $title => $data) { if($data) $body .= '
    ' . _($title) . '
    '; } echo Element('page.html', array( 'config'=>$config, 'title'=>_('Dashboard'), 'body'=>$body, '__mod'=>true )); } elseif(preg_match('/^\/logout$/', $query)) { destroyCookies(); header('Location: ?/', true, $config['redirect_http']); } elseif(preg_match('/^\/confirm\/(.+)$/', $query, $matches)) { $uri = &$matches[1]; $body = '

    ' . 'Are you sure you want to do that?' . 'We were unable to serve a confirmation dialog for ' . '?/' . utf8tohtml($uri) . '' . ', probably due to Javascript being disabled.' . '

    ' . '

    Confirm

    '; echo Element('page.html', array( 'config'=>$config, 'title'=>'Confirm', 'body'=>$body, 'mod'=>true ) ); } elseif(preg_match('/^\/upgrade$/', $query)) { if($mod['type'] != ADMIN) error($config['error']['noaccess']); if(is_dir('.git')) { // use git instead $body = '

    git pull

    '; $body .= '

    ' . str_replace("\n", '
    ', shell_exec('git pull')) . '

    '; $body .= '
    '; echo Element('page.html', array( 'config' => $config, 'title' => 'Upgraded', 'body' => $body )); exit; } if(!extension_loaded('curl')) error('You need the cURL PHP extension to do that.'); if(!class_exists('ZipArchive')) error('You need the ZipArchive class to do that.'); if(!in_array('zip', stream_get_wrappers())) error('You need the zip:// stream wrapper to do that.'); $temp = tempnam($config['tmp'], 'tinyboard'); $fp = fopen($temp, 'w+'); $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, 'https://github.com/savetheinternet/Tinyboard/zipball/master'); curl_setopt($curl, CURLOPT_FAILONERROR, true); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($curl, CURLOPT_TIMEOUT, 45); curl_setopt($curl, CURLOPT_FILE, $fp); curl_setopt($curl, CURLOPT_WRITEHEADER, $header = tmpfile()); curl_setopt($curl, CURLOPT_HEADER, true); curl_exec($curl); if(curl_errno($curl)) error('Failed downloading newest revision: ' . curl_error($curl)); curl_close($curl); fflush($fp); fclose($fp); fseek($header, 0); $version = false; while($line = fgets($header)) { if(preg_match('/^Content-Disposition: attachment; filename=savetheinternet-Tinyboard-(.+)\.zip\s?$/', $line, $m)) { $version = $m[1]; } } fclose($header); $zip = new ZipArchive(); if(!$zip->open($temp)) error('Could not make sense of the ZIP archive.'); $version = preg_replace('/^savetheinternet-Tinyboard-(\w+)\//', '$1', $dir = $zip->getNameIndex(0)); $errors = array(); for($i = 1; $i < $zip->numFiles; $i++) { $filename = str_replace($dir, '', $zip->getNameIndex($i)); if($filename == 'inc/instance-config.php') continue; // don't override config // are we able to write here? if(!((file_exists($filename) && is_writable($filename)) || (!file_exists($filename) && is_writable(dirname($filename))))) { // nope $errors[] = 'Cannot write to ' . $filename . '!'; } } $zip->close(); if($errors) { $body = '

    Error(s) upgrading

    Tinyboard can not self-upgrade until the following is fixed:

    Please fix the above errors and refresh to try again.

    '; unlink($temp); echo Element('page.html', array( 'config' => $config, 'title' => 'Error(s) upgrading', 'body' => $body )); exit; } // For some reason, reading the ZIP entries in PHP doesn't seem to work very well. // Use shell instead. shell_exec('TEMP_DIR=$(mktemp -d); unzip -q ' . escapeshellarg($temp) . ' -d $TEMP_DIR -x "' . escapeshellarg($dir) . 'inc/instance-config.php"; mv -v $TEMP_DIR/' . escapeshellarg($dir) . '* "' . getcwd() . '"; rm -rf $TEMP_DIR'); unlink($temp); echo Element('page.html', array( 'config' => $config, 'title' => 'Upgraded', 'body' => '

    Upgrading seems to have gone okay. You are now at revision ' . $version . '.

    ' )); } elseif(preg_match('/^\/log(\/(\d+))?$/', $query, $match)) { if(!hasPermission($config['mod']['modlog'])) error($config['error']['noaccess']); $page = isset($match[2]) ? $match[2] : 1; $boards = array(); $_boards = listBoards(); foreach($_boards as &$_b) { $boards[$_b['id']] = $_b['uri']; } $query = prepare("SELECT `mod` as `id`, `username`, `ip`, `board`, `time`, `text` FROM `modlogs` LEFT JOIN `mods` ON `mod` = `mods`.`id` ORDER BY `time` DESC LIMIT :offset, :limit"); $query->bindValue(':limit', $config['mod']['modlog_page'], PDO::PARAM_INT); $query->bindValue(':offset', ($page - 1) * $config['mod']['modlog_page'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); if(!$query->rowCount()) { $body = '

    (Nothing to display.)

    '; } else { $body = '' . '' . '' . '' . '' . '' . '' . ''; while($log = $query->fetch()) { $log_id = 'log_' . md5($log['text']); if($config['cache']['enabled'] && $_log = cache::get($log_id)) $log['text'] = $_log; else { $log['text'] = utf8tohtml($log['text']); $log['text'] = preg_replace('/(\d+\.\d+\.\d+\.\d+)/', '$1', $log['text']); if(isset($boards[$log['board']])) { if(preg_match('/post #(\d+)/', $log['text'], $match)) { $post_query = prepare(sprintf("SELECT `thread` FROM `posts_%s` WHERE `id` = :id", $boards[$log['board']])); $post_query->bindValue(':id', $match[1], PDO::PARAM_INT); $post_query->execute() or error(db_error($query)); if($post = $post_query->fetch()) { $log['text'] = preg_replace('/post (#(\d+))/', 'post $1', $log['text']); } else { $log['text'] = preg_replace('/post (#(\d+))/', 'post $1', $log['text']); } if($config['cache']['enabled']) cache::set($log_id, $log['text']); } } } $body .= '' . '' . '' . '' . '' : '-') . '' . ''; } $body .= '
    ' . _('User') . '' . _('IP address') . '' . _('Ago') . '' . _('Board') . '' . _('Action') . '
    ' . ($log['username'] ? '' . $log['username'] . '' : '' . ($log['id'] < 0 ? 'system' : 'deleted?') . '') . '' . ($log['id'] < 0 ? '–' : '' . $log['ip'] . '') . '' . ago($log['time']) . '' . ($log['board'] ? '' . sprintf($config['board_abbreviation'], $log['board']) . '' . $log['text'] . '
    '; $query = prepare("SELECT COUNT(*) AS `count` FROM `modlogs`"); $query->execute() or error(db_error($query)); $count = $query->fetch(); $body .= '

    '; for($x = 0; $x < $count['count'] / $config['mod']['modlog_page']; $x ++) { $body .= '[' . ($x + 1) . '] '; } $body .= '

    '; } echo Element('page.html', array( 'config'=>$config, 'title'=>_('Moderation log'), 'body'=>$body, 'mod'=>true ) ); } elseif(preg_match('/^\/themes\/none$/', $query, $match)) { if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); // Clearsettings query("TRUNCATE TABLE `theme_settings`") or error(db_error()); echo Element('page.html', array( 'config'=>$config, 'title'=>'No theme', 'body'=>'

    Successfully uninstalled all themes.

    ' . '

    Go back to themes.

    ', 'mod'=>true ) ); } elseif(preg_match('/^\/themes\/([\w\-]+)\/rebuild$/', $query, $match)) { if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); rebuildTheme($match[1], 'all'); echo Element('page.html', array( 'config'=>$config, 'title'=>'Rebuilt', 'body'=>'

    Successfully rebuilt the ' . $match[1] . ' theme.

    ' . '

    Go back to themes.

    ', 'mod'=>true ) ); } elseif(preg_match('/^\/themes\/(\w+)\/uninstall$/', $query, $match)) { if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); $query = prepare("DELETE FROM `theme_settings` WHERE `theme` = :theme"); $query->bindValue(':theme', $match[1]); $query->execute() or error(db_error($query)); echo Element('page.html', array( 'config'=>$config, 'title'=>'Uninstalled', 'body'=>'

    Successfully uninstalled the ' . $match[1] . ' theme.

    ' . '

    Go back to themes.

    ', 'mod'=>true ) ); } elseif(preg_match('/^\/themes(\/([\w\-]+))?$/', $query, $match)) { if(!hasPermission($config['mod']['themes'])) error($config['error']['noaccess']); if(!is_dir($config['dir']['themes'])) error(_('Themes directory doesn\'t exist!')); if(!$dir = opendir($config['dir']['themes'])) error(_('Cannot open themes directory; check permissions.')); if(isset($match[2])) { $_theme = &$match[2]; if(!$theme = loadThemeConfig($_theme)) { error($config['error']['invalidtheme']); } if(isset($_POST['install'])) { // Check if everything is submitted foreach($theme['config'] as &$c) { if(!isset($_POST[$c['name']]) && $c['type'] != 'checkbox') error(sprintf($config['error']['required'], $c['title'])); } // Clear previous settings $query = prepare("DELETE FROM `theme_settings` WHERE `theme` = :theme"); $query->bindValue(':theme', $_theme); $query->execute() or error(db_error($query)); foreach($theme['config'] as &$c) { $query = prepare("INSERT INTO `theme_settings` VALUES(:theme, :name, :value)"); $query->bindValue(':theme', $_theme); $query->bindValue(':name', $c['name']); $query->bindValue(':value', $_POST[$c['name']]); $query->execute() or error(db_error($query)); } $query = prepare("INSERT INTO `theme_settings` VALUES(:theme, NULL, NULL)"); $query->bindValue(':theme', $_theme); $query->execute() or error(db_error($query)); $result = true; $body = ''; if(isset($theme['install_callback'])) { $ret = $theme['install_callback'](themeSettings($_theme)); if($ret && !empty($ret)) { if(is_array($ret) && count($ret) == 2) { $result = $ret[0]; $ret = $ret[1]; } $body .= '
    ' . $ret . '
    '; } } if($result) { $body .= '

    Successfully installed and built theme.

    '; } else { // install failed $query = prepare("DELETE FROM `theme_settings` WHERE `theme` = :theme"); $query->bindValue(':theme', $_theme); $query->execute() or error(db_error($query)); } $body .= '

    Go back to themes.

    '; // Build themes rebuildThemes('all'); echo Element('page.html', array( 'config'=>$config, 'title'=>($result ? 'Installed "' . utf8tohtml($theme['name']) . '"' : 'Installation failed!'), 'body'=>$body, 'mod'=>true ) ); } else { $body = '
    '; if(!isset($theme['config']) || empty($theme['config'])) { $body .= '

    (No configuration required.)

    '; } else { $settings = themeSettings($_theme); $body .= ''; foreach($theme['config'] as &$c) { $body .= ''; } $body .= '
    ' . $c['title'] . ''; switch($c['type']) { case 'text': default: $body .= ''; } if(isset($c['comment'])) $body .= ' ' . $c['comment'] . ''; $body .= '
    '; } $body .= '

    '; echo Element('page.html', array( 'config'=>$config, 'title'=>'Installing "' . utf8tohtml($theme['name']) . '"', 'body'=>$body, 'mod'=>true ) ); } } else { $themes_in_use = array(); $query = query("SELECT `theme` FROM `theme_settings` WHERE `name` IS NULL AND `value` IS NULL") or error(db_error()); while($theme = $query->fetch()) { $themes_in_use[$theme['theme']] = true; } // Scan directory for themes $themes = array(); while($file = readdir($dir)) { if($file[0] != '.' && is_dir($config['dir']['themes'] . '/' . $file)) { $themes[] = $file; } } closedir($dir); $body = ''; if(empty($themes)) { $body = '

    (No themes installed.)

    '; } else { $body .= ''; foreach($themes as &$_theme) { $theme = loadThemeConfig($_theme); markup($theme['description']); $body .= '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . ''; } $body .= '
    ' . _('Name') . '' . utf8tohtml($theme['name']) . '
    ' . _('Version') . '' . utf8tohtml($theme['version']) . '
    ' . _('Description') . '' . $theme['description'] . '
    ' . _('Thumbnail') . '
    ' . _('Actions') . '

    '; } if(!empty($themes_in_use)) $body .= '

    ' . _('Uninstall all themes.') . '

    '; echo Element('page.html', array( 'config'=>$config, 'title'=>_('Manage themes'), 'body'=>$body, 'mod'=>true ) ); } } elseif(preg_match('/^\/noticeboard\/delete\/(\d+)$/', $query, $match)) { if(!hasPermission($config['mod']['noticeboard_delete'])) error($config['error']['noaccess']); $query = prepare("DELETE FROM `noticeboard` WHERE `id` = :id"); $query->bindValue(':id', $match[1], PDO::PARAM_INT); $query->execute() or error(db_error($query)); if($config['cache']['enabled']) cache::delete('noticeboard_preview'); header('Location: ?/noticeboard', true, $config['redirect_http']); } elseif(preg_match('/^\/noticeboard$/', $query)) { if(!hasPermission($config['mod']['noticeboard'])) error($config['error']['noaccess']); $body = ''; if(hasPermission($config['mod']['noticeboard_post']) && isset($_POST['subject']) && isset($_POST['body']) && !empty($_POST['body'])) { $query = prepare("INSERT INTO `noticeboard` VALUES (NULL, :mod, :time, :subject, :body)"); $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); $query->bindvalue(':time', time(), PDO::PARAM_INT); $query->bindValue(':subject', utf8tohtml($_POST['subject'])); markup($_POST['body']); $query->bindValue(':body', $_POST['body']); $query->execute() or error(db_error($query)); if($config['cache']['enabled']) cache::delete('noticeboard_preview'); header('Location: ?/noticeboard#' . $pdo->lastInsertId(), true, $config['redirect_http']); } else { if(hasPermission($config['mod']['noticeboard_post'])) { $body .= '
    New post
    ' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '' . '
    ' . $mod['username'] . '
    ' . _('Subject') . '
    ' . _('Body') . '
    ' . '
    '; } $query = prepare("SELECT `noticeboard`.*, `username` FROM `noticeboard` LEFT JOIN `mods` ON `mods`.`id` = `mod` ORDER BY `id` DESC LIMIT :limit"); $query->bindValue(':limit', $config['mod']['noticeboard_display'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); while($notice = $query->fetch()) { $body .= '
    ' . (hasPermission($config['mod']['noticeboard_delete']) ? '[delete]' : '') . '

    ' . ($notice['subject'] ? $notice['subject'] : '' . _('no subject') . '' ) . ' — by ' . (isset($notice['username']) ? utf8tohtml($notice['username']) : '???' ) . ' at ' . strftime($config['post_date'], $notice['time']) . '

    ' . $notice['body'] . '

    '; } echo Element('page.html', array( 'config'=>$config, 'title'=>_('Noticeboard'), 'body'=>$body, 'mod'=>true ) ); } } elseif(preg_match('/^\/news\/delete\/(\d+)$/', $query, $match)) { if(!hasPermission($config['mod']['noticeboard_delete'])) error($config['error']['noaccess']); $query = prepare("DELETE FROM `news` WHERE `id` = :id"); $query->bindValue(':id', $match[1], PDO::PARAM_INT); $query->execute() or error(db_error($query)); rebuildThemes('news'); header('Location: ?/news', true, $config['redirect_http']); } elseif(preg_match('/^\/news$/', $query)) { $body = ''; if(hasPermission($config['mod']['news'])) { if(isset($_POST['subject']) && isset($_POST['body']) && !empty($_POST['body'])) { $query = prepare("INSERT INTO `news` VALUES (NULL, :name, :time, :subject, :body)"); if(isset($_POST['name']) && hasPermission($config['mod']['news_custom'])) $name = &$_POST['name']; else $name = &$mod['username']; $query->bindValue(':name', utf8tohtml($name), PDO::PARAM_INT); $query->bindvalue(':time', time(), PDO::PARAM_INT); $query->bindValue(':subject', utf8tohtml($_POST['subject'])); markup($_POST['body']); $query->bindValue(':body', $_POST['body']); $query->execute() or error(db_error($query)); rebuildThemes('news'); } $body .= '
    New post
    ' . '' . '' . (hasPermission($config['mod']['news_custom']) ? '' : '') . '' . '' . '' . '' . '' . '' . '' . '' . '
    ' . _('Name') . '' . $mod['username'] . '
    ' . _('Subject') . '
    ' . _('Body') . '
    ' . '
    '; } $query = prepare("SELECT * FROM `news` ORDER BY `id` DESC LIMIT :limit"); $query->bindValue(':limit', $config['mod']['noticeboard_display'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); while($news = $query->fetch()) { $body .= '
    ' . (hasPermission($config['mod']['news_delete']) ? '[delete]' : '') . '

    ' . ($news['subject'] ? $news['subject'] : '' . _('no subject') . '' ) . ' — by ' . $news['name'] . ' at ' . strftime($config['post_date'], $news['time']) . '

    ' . $news['body'] . '

    '; } echo Element('page.html', array( 'config'=>$config, 'title'=>_('News'), 'body'=>$body, 'mod'=>true ) ); } elseif(preg_match('/^\/inbox\/readall$/', $query, $match)) { $query = prepare("UPDATE `pms` SET `unread` = 0 WHERE `to` = :id"); $query->bindValue(':id', $mod['id'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); modLog('Marked all PMs as read'); header('Location: ?/inbox', true, $config['redirect_http']); } elseif(preg_match('/^\/inbox$/', $query, $match)) { $query = prepare("SELECT `unread`,`pms`.`id`, `time`, `sender`, `to`, `message`, `username` FROM `pms` LEFT JOIN `mods` ON `mods`.`id` = `sender` WHERE `to` = :mod ORDER BY `unread` DESC, `time` DESC"); $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); if($query->rowCount() == 0) { $body = '

    (' . _('No private messages for you.') . ')

    '; } else { $unread_pms = 0; $body = ''; while($pm = $query->fetch()) { $body .= '' . '' . '' . '' . '' . ''; if($pm['unread']) $unread_pms++; } $body .= '
    IDFromDateMessage snippet
    ' . $pm['id'] . '' . ($pm['username'] ? '' . $pm['username'] . '' : 'deleted?') . '' . strftime($config['post_date'], $pm['time']) . '' . pm_snippet($pm['message']) . '
    '; if($unread_pms) { $body = '

    (Mark all as read)

    ' . $body; } } echo Element('page.html', array( 'config'=>$config, 'title'=>_('PM Inbox') . ' (' . ($query->rowCount() == 0 ? _('empty') : $unread_pms . ' ' . _('unread')) . ')', 'body'=>$body, 'mod'=>true ) ); } elseif(preg_match('/^\/PM\/(\d+)$/', $query, $match)) { $id = &$match[1]; if(hasPermission($config['mod']['master_pm'])) { $query = prepare("SELECT `pms`.`id`, `time`, `sender`, `unread`, `to`, `message`, `username` FROM `pms` LEFT JOIN `mods` ON `mods`.`id` = `sender` WHERE `pms`.`id` = :id"); } else { $query = prepare("SELECT `pms`.`id`, `time`, `sender`, `unread`, `to`, `message`, `username` FROM `pms` LEFT JOIN `mods` ON `mods`.`id` = `sender` WHERE `pms`.`id` = :id AND `to` = :mod"); $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); } $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); if(!$pm = $query->fetch()) { // Mod doesn't exist error($config['error']['404']); } if(isset($_POST['delete'])) { $query = prepare("DELETE FROM `pms` WHERE `id` = :id"); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); modLog('Deleted a PM'); header('Location: ?/inbox', true, $config['redirect_http']); } else { if($pm['unread']) { $query = prepare("UPDATE `pms` SET `unread` = 0 WHERE `id` = :id"); $query->bindValue(':id', $id, PDO::PARAM_INT); $query->execute() or error(db_error($query)); modLog('Read a PM'); } if($pm['to'] != $mod['id']) { $query = prepare("SELECT `username` FROM `mods` WHERE `id` = :id"); $query->bindValue(':id', $pm['to'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); if($_mod = $query->fetch()) { $__to = &$_mod['username']; } else { $__to = false; } } $body = '
    ' . '' . (isset($__to) ? '' : '') . '' . '' . '
    From' . (!$pm['username'] ? '???' : '' . utf8tohtml($pm['username']) . '' ) . '
    To' . ($__to === false ? '???' : '' . utf8tohtml($__to) . '' ) . '
    Date ' . strftime($config['post_date'], $pm['time']) . '
    Message ' . $pm['message'] . '
    ' . '

    ' . '
    ' . '

    Reply with quote

    '; echo Element('page.html', array( 'config'=>$config, 'title'=>'Private message', 'body'=>$body, 'mod'=>true ) ); } } elseif(preg_match('/^\/new_PM\/(\d+)(\/(\d+))?$/', $query, $match)) { if(!hasPermission($config['mod']['create_pm'])) error($config['error']['noaccess']); $to = &$match[1]; $query = prepare("SELECT `username`,`id` FROM `mods` WHERE `id` = :id"); $query->bindValue(':id', $to, PDO::PARAM_INT); $query->execute() or error(db_error($query)); if(!$to = $query->fetch()) { // Mod doesn't exist error($config['error']['404']); } if(isset($_POST['message'])) { // Post message $message = &$_POST['message']; if(empty($message)) error($config['error']['tooshort_body']); markup($message); $query = prepare("INSERT INTO `pms` VALUES (NULL, :sender, :to, :message, :time, 1)"); $query->bindValue(':sender', $mod['id'], PDO::PARAM_INT); $query->bindValue(':to', $to['id'], PDO::PARAM_INT); $query->bindValue(':message', $message); $query->bindValue(':time', time(), PDO::PARAM_INT); $query->execute() or error(db_error($query)); modLog('Sent a PM to ' . $to['username']); echo Element('page.html', array( 'config'=>$config, 'title'=>'PM sent', 'body'=>'

    Message sent successfully to ' . utf8tohtml($to['username']) . '.

    ', 'mod'=>true ) ); } else { $value = ''; if(isset($match[3])) { $reply = &$match[3]; $query = prepare("SELECT `message` FROM `pms` WHERE `sender` = :sender AND `to` = :mod AND `id` = :id"); $query->bindValue(':sender', $to['id'], PDO::PARAM_INT); $query->bindValue(':mod', $mod['id'], PDO::PARAM_INT); $query->bindValue(':id', $reply, PDO::PARAM_INT); $query->execute() or error(db_error($query)); if($pm = $query->fetch()) { $value = quote($pm['message']); } } $body = '
    ' . '' . '' . '' . '
    To' . (hasPermission($config['mod']['editusers']) ? '' . utf8tohtml($to['username']) . '' : utf8tohtml($to['username']) ) . '
    Message
    ' . '

    ' . '
    '; echo Element('page.html', array( 'config'=>$config, 'title'=>'New PM for ' . utf8tohtml($to['username']), 'body'=>$body, 'mod'=>true ) ); } } elseif(preg_match('/^\/search$/', $query)) { if(!hasPermission($config['mod']['search'])) error($config['error']['noaccess']); $body = '

    Search

    ' . '

    ' . '' . '' . '

    ' . '

    (Search is case-insensitive, and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.)

    ' . '
    '; if(isset($_POST['search']) && !empty($_POST['search'])) { $phrase = &$_POST['search']; $_body = ''; $filters = array(); function search_filters($m) { global $filters; $name = $m[2]; $value = isset($m[4]) ? $m[4] : $m[3]; if(!in_array($name, array('id', 'thread', 'subject', 'email', 'name', 'trip', 'capcode', 'filename', 'filehash', 'ip'))) { // unknown filter return $m[0]; } $filters[$name] = $value; return $m[1]; } $phrase = trim(preg_replace_callback('/(^|\s)(\w+):("(.*)?"|[^\s]*)/', 'search_filters', $phrase)); // Escape escape character $phrase = str_replace('!', '!!', $phrase); // Remove SQL wildcard $phrase = str_replace('%', '!%', $phrase); // Use asterisk as wildcard to suit convention $phrase = str_replace('*', '%', $phrase); $like = ''; $match = array(); // Find exact phrases if(preg_match_all('/"(.+?)"/', $phrase, $m)) { foreach($m[1] as &$quote) { $phrase = str_replace("\"{$quote}\"", '', $phrase); $match[] = $pdo->quote($quote); } } $words = explode(' ', $phrase); foreach($words as &$word) { if(empty($word)) continue; $match[] = $pdo->quote($word); } $like = ''; foreach($match as &$phrase) { if(!empty($like)) $like .= ' AND '; $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase); $like .= '`body` LIKE ' . $phrase . ' ESCAPE \'!\''; } foreach($filters as $name => $value) { if(!empty($like)) $like .= ' AND '; $like .= '`' . $name . '` = '. $pdo->quote($value); } $like = str_replace('%', '%%', $like); $boards = listBoards(); foreach($boards as &$_b) { openBoard($_b['uri']); $query = prepare(sprintf("SELECT * FROM `posts_%s` WHERE " . $like . " ORDER BY `time` DESC LIMIT :limit", $board['uri'])); $query->bindValue(':limit', $config['mod']['search_results'], PDO::PARAM_INT); $query->execute() or error(db_error($query)); $temp = ''; while($post = $query->fetch()) { if(!$post['thread']) { $po = new Thread($post['id'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'], $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'], $post['filename'], $post['ip'], $post['sticky'], $post['locked'], $post['sage'], $post['embed'], '?/', $mod, false); } else { $po = new Post($post['id'], $post['thread'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'], $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'], $post['filename'], $post['ip'], $post['embed'], '?/', $mod); } $temp .= $po->build(true) . '
    '; } if(!empty($temp)) $_body .= '
    ' . $query->rowCount() . ' result' . ($query->rowCount() != 1 ? 's' : '') . ' on ' . sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] . '' . $temp . '
    '; } $body .= '
    '; if(!empty($_body)) $body .= $_body; else $body .= '

    (No results.)

    '; } echo Element('page.html', array( 'config'=>$config, 'title'=>'Search', 'body'=>$body, 'mod'=>true ) ); } elseif(preg_match('/^\/users$/', $query)) { if(!hasPermission($config['mod']['manageusers'])) error($config['error']['noaccess']); $body = '
    '; $query = query("SELECT *, (SELECT `time` FROM `modlogs` WHERE `mod` = `id` ORDER BY `time` DESC LIMIT 1) AS `last`, (SELECT `text` FROM `modlogs` WHERE `mod` = `id` ORDER BY `time` DESC LIMIT 1) AS `action` FROM `mods` ORDER BY `type` DESC,`id`") or error(db_error()); while($_mod = $query->fetch()) { $type = $_mod['type'] == JANITOR ? 'Janitor' : ($_mod['type'] == MOD ? 'Mod' : 'Admin'); $_mod['boards'] = explode(',', $_mod['boards']); foreach($_mod['boards'] as &$_board) { if($_board != '*') $_board = '/' . $_board . '/'; } $body .= '' . '' . '' . '' . '' . '' . ''; } $body .= '
    ' . _('ID') . '' . _('Username') . '' . _('Type') . '' . _('Boards') . '' . _('Last action') . '
    ' . $_mod['id'] . '' . utf8tohtml($_mod['username']) . '' . $type . '' . implode(', ', $_mod['boards']) . '' . ($_mod['last'] ? (hasPermission($config['mod']['modlog']) ? '' . ago($_mod['last']) . '' : ago($_mod['last'])) : 'never') . '' . (hasPermission($config['mod']['promoteusers']) ? ($_mod['type'] != ADMIN ? '' :'') . ($_mod['type'] != JANITOR ? '' :'') : '' ) . (hasPermission($config['mod']['editusers']) || (hasPermission($config['mod']['change_password']) && $_mod['id'] == $mod['id'])? '[edit]' : '' ) . (hasPermission($config['mod']['create_pm']) ? '[PM]' : '' ) . '
    '; if(hasPermission($config['mod']['createusers'])) { $body .= '

    ' . _('Create new user') . '

    '; } $body .= '
    '; echo Element('page.html', array( 'config'=>$config, 'title'=>_('Manage users'), 'body'=>$body ,'mod'=>true ) ); } elseif(preg_match('/^\/users\/new$/', $query)) { if(!hasPermission($config['mod']['createusers'])) error($config['error']['noaccess']); if(isset($_POST['username']) && isset($_POST['password'])) { if(!isset($_POST['type'])) { error(sprintf($config['error']['required'], 'type')); } if($_POST['type'] != ADMIN && $_POST['type'] != MOD && $_POST['type'] != JANITOR) { error(sprintf($config['error']['invalidfield'], 'type')); } // Check if already exists $query = prepare("SELECT `id` FROM `mods` WHERE `username` = :username"); $query->bindValue(':username', $_POST['username']); $query->execute() or error(db_error($query)); if($_mod = $query->fetch()) { error(sprintf($config['error']['modexists'], $_mod['id'])); } $boards = array(); foreach($_POST as $name => $null) { if(preg_match('/^board_(.+)$/', $name, $m)) $boards[] = $m[1]; } $boards = implode(',', $boards); $query = prepare("INSERT INTO `mods` VALUES (NULL, :username, :password, :type, :boards)"); $query->bindValue(':username', $_POST['username']); $query->bindValue(':password', sha1($_POST['password'])); $query->bindValue(':type', $_POST['type'], PDO::PARAM_INT); $query->bindValue(':boards', $boards); $query->execute() or error(db_error($query)); modLog('Create a new user: "' . $_POST['username'] . '"'); header('Location: ?/users', true, $config['redirect_http']); } else { $__boards = '