. // //-------------------- // // Script Description: // // Free / Open Source PHP comment system intended to replace Disqus. // Allows completely anonymous comments to be posted, the only // required information is the comment itself. The comments are stored // as individual XML files, example: "1.xml" is the first comment, // "2.xml" is the second, and "1-1.xml" is first reply to the first // comment, "1-2.xml" is the second reply, and so on. // // Features restricted use of HTML tags, automatic URL links, avatar // icons, replies, comment editing and deletion, notification emails, // comment RSS feeds, likes, popular comments, customizable CSS, // referrer checking, and permalinks. // // // Installation: // // 1. Save this source code as "comments.php". // 2. Download these files: // http://www.tildehash.com/comments/scripts/secrets.php, // http://www.tildehash.com/comments/scripts/like.php?source, // http://www.tildehash.com/comments/scripts/avatars.php?source, // http://www.tildehash.com/comments/images/avatar.png, // http://www.tildehash.com/comments/images/delicon.png, // http://www.tildehash.com/comments/images/edit.png, // http://www.tildehash.com/comments/images/email.png, // http://www.tildehash.com/comments/images/has-email.png, // http://www.tildehash.com/comments/images/like.png, // http://www.tildehash.com/comments/images/liked.png, // http://www.tildehash.com/comments/images/name.png, // http://www.tildehash.com/comments/images/no-email.png, // http://www.tildehash.com/comments/images/password.png, // http://www.tildehash.com/comments/images/rss.png, // http://www.tildehash.com/comments/images/website.png, // http://www.tildehash.com/comments/comments.css, // http://www.tildehash.com/comments/template.xml // // 3. Give all said files permissions "0755" (readable & executable by all). // 4. Place "comments.php" at your website's highest level, typically "/". // 5. Create a directory called "comments" at your website's highest level. // 6. Create a directory called "pages" under the "comments" directory. // 7. Give "pages" directory permission "0777" (readable, writable & executable by all). // 8. Add the following HTML tags to pages that are to have comments: // // // // // Optionally, you may set the "display" URL query to the page filename to // display only a comment count, ex. state-of-firefox-4-0-on-gnu-linux.html // will display something like "6 Comments (9 counting replies)". // // You may also use the following JavaScript tag with any or all of // the following variables to change the comment system's behavior: // // // // // Change Log file (please record your modifications to this code): // http://www.tildehash.com/comments/changelog.txt // // Display source code if (basename($_SERVER['PHP_SELF']) == basename(__FILE__)) { $script_query = 'true'; if (!isset($_GET['rss']) and !isset($_GET['canon_url'])) { if (isset($_GET['source']) or !isset($_SERVER['HTTP_REFERER'])) { header('Content-type: text/plain'); exit(file_get_contents(basename(__FILE__))); } } } // Default Settings $root_dir = '/comments/'; // HTTP root directory for comments $name = 'GNU Knows Who'; // Nickname when one isn't given $template = 'default'; // Comment layout template $page_title = 'yes'; // Whether page title is shown or not $short_dates = 'yes'; // Whether comment dates are shortened $icons = 'yes'; // Whether comments have avatar icons (Gravatar) $icon_size = '45'; // Size of Gravatar icons in pixels $indentation = 'left'; // Side to add comment indentation on $rows = '5'; // Default comment box height in rows $popular = '5'; // Minimum likes a comment needs to be popular $top_cmts = '2'; // Number of comments allowed to become popular $comment_form = 'Type Comment Here (other fields optional)'; // "Comment" field's default text $reply_form = 'Type Reply Here (other fields optional)'; // "Reply" field's default text $post_button = 'Post Comment'; // "Post Comment" button's default text $del_note = 'This comment has been deleted.'; // Notice of deleted comment $ip_addrs = 'yes'; // Whether to store users' IP addresses $expire = time()+60*60*24*30; // Cookies' expiration date $domain = $_SERVER['HTTP_HOST']; // Domain name for refer checking & notifications $mode = (isset($mode)) ? $mode : 'javascript'; // Content output type chdir (dirname(__FILE__) . $root_dir); // Change to root directory // Switch between mostly-PHP and JavaScript+PHP modes function jsAddSlashes($script, $type = '') { global $mode; if ($mode == 'javascript') { if ($type != 'single') { return 'show_cmt += \'' . str_replace(array('\\\n', '\\\r', '\\\\n', "\'+", "+\'", "\t"), array('\n', '\r', '\\n', "'+", "+'", ''), addcslashes($script, "'")) . '\';' . PHP_EOL; break; } else { return 'document.write("' . str_replace(array('\\\n', '\\\r', '\"+', '+\"'), array('\n', '\r', '"+', '+"'), addslashes($script)) . '");' . PHP_EOL; break; } } else { return str_replace(array('\n', '\r'), '', $script) . PHP_EOL; } } // Include encryption key & notification e-mail, error on fail if (!include('scripts/secrets.php')) { if (empty($enotify) and empty($key)) { exit(jsAddSlashes('HashOver - Error: "scripts/secrets.php" file is required (with permission 0755)', 'single')); } } // Script execution starting time $exec_time = explode(' ', microtime()); $exec_start = $exec_time[1] + $exec_time[0]; // Set Canonical URL if (!isset($canon_url) and isset($script_query)) { if (isset($_POST['canon_url']) and !empty($_POST['canon_url'])) { $canon_url = (preg_match('/([http|https]):\/\//i', $_POST['canon_url'])) ? $_POST['canon_url'] : 'http://' . $_POST['canon_url']; } else if (isset($_GET['canon_url']) and !empty($_GET['canon_url'])) { $canon_url = (preg_match('/([http|https]):\/\//i', $_GET['canon_url'])) ? $_GET['canon_url'] : 'http://' . $_GET['canon_url']; } } // Get full page URL or Canonical URL if ($mode == 'javascript') { if (isset($_SERVER['HTTP_REFERER']) and !isset($_GET['rss'])) { // Check if the script was requested by this server if (!preg_match('/' . $domain . '/i', parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST))) { exit(jsAddSlashes('HashOver - Error: External use not allowed.', 'single')); } $page_url = (!isset($canon_url) or empty($canon_url)) ? $_SERVER['HTTP_REFERER'] : $canon_url; } else { if (!isset($_GET['rss'])) { exit(jsAddSlashes('HashOver - Error: No way to get page URL, HTTP referrer not set.', 'single')); } else { $page_url = $_GET['rss']; } } } else { $page_url = (!isset($canon_url) or empty($canon_url)) ? 'http://' . $domain . $_SERVER['REQUEST_URI'] : $canon_url; } // Set URL to "count_link" query value if (isset($script_query)) { if (isset($_GET['count_link']) and !empty($_GET['count_link'])) { $page_url = $_GET['count_link']; } } // Clean URL for comment thread directory name $parse_url = parse_url($page_url); // Turn page URL into array $ref_path = ($parse_url['path'] == '/') ? 'index' : str_replace(array('/', '.', '='), '-', substr($parse_url['path'], 1)); // Remove unwanted URL queries if (file_exists('ignore_queries.txt') and isset($parse_url['query'])) { $ignore_queries = explode(PHP_EOL, file_get_contents('ignore_queries.txt')); $ref_queries = explode('&', $parse_url['query']); $parse_url['query'] = ''; for ($q = 0; $q <= (count($ref_queries) - 1); $q++) { if (!in_array($ref_queries[$q], $ignore_queries) and !empty($ref_queries[$q])) { if (!in_array(basename($ref_queries[$q], '=' . end(explode('=', $ref_queries[$q]))), $ignore_queries)) { $parse_url['query'] .= ($q > 0 and !empty($parse_url['query'])) ? '&' . $ref_queries[$q] : $ref_queries[$q]; } } } } // Append URL query to file path if (!empty($parse_url['query'])) { $ref_path .= '-' . str_replace(array('/', '.', '='), '-', $parse_url['query']); } // Page comments directory $http_dir = $root_dir . 'pages/' . $ref_path; $dir = ($ref_path != 'comments-php') ? 'pages/' . $ref_path : exit(jsAddSlashes('HashOver - Error: Failure setting comment directory name')); // Create comment thread directory & error on fail if (!file_exists($dir) and !isset($_GET['count_link'])) { if (!mkdir($dir, 0777) and !chmod($dir, 0777)) { exit(jsAddSlashes('HashOver - Error: Failed to create comment thread directory at "' . $dir . '"', 'single')); } } // If the "count_link" query is set, display link to comment if (isset($script_query)) { if (isset($_GET['count_link']) and !empty($_GET['count_link'])) { if (!file_exists($dir)) { exit(jsAddSlashes('Post Comment', 'single')); } } } // Encryption method for storing e-mails function encrypt($str) { global $key; $str = str_replace(' ', '-', $str); $key = str_replace(chr(32), '', $key); if (strlen($key) < 8) exit(jsAddSlashes('HashOver - Error: Key error, make sure it\'s at least 8 characters long.', 'single')); $kl = strlen($key) < 32 ? strlen($key) : 32; $k = array(); for ($i2 = 0; $i2 < $kl; $i2++) { $k[$i2] = ord($key{$i2}) & 0x1F; } $j = 0; for ($i2 = 0; $i2 < strlen($str); $i2++) { $e = ord($str{$i2}); $str{$i2} = $e & 0xE0 ? chr($e^$k[$j]) : chr($e); $j++; $j = $j == $kl ? 0 : $j; } return $str; } $html_template = file_get_contents('templates/' . $template . '.html'); // Load HTML template $top_likes = array(); // For sorting top comments // Read comment files, and wrap them in HTML divs function parse_comments($file, $variable, $check) { global $root_dir, $ref_path, $html_template, $icons, $icon_size, $short_dates, $top_likes, $popular, $domain, $indentation, $script_query; // Generate permalink $permalink = 'c' . str_replace('-', 'r', basename($file, '.xml')); $permatext = end(explode('-', basename($file, '.xml'))); // Calculate CSS padding for reply indentation if (($dashes = substr_count(basename($file), '-')) != '0' and $check == 'yes') { $indent = ($icon_size + 20) * $dashes; } else { $indent = '0'; } if (!isset($_GET['count_link']) or !isset($script_query)) { if (($read_cmt = @simplexml_load_file($file)) !== false) { $html_output = $html_template; $permalink .= ($check == 'yes') ? '' : '-pop'; if ($read_cmt['likes'] >= $popular) $top_likes["{$read_cmt['likes']}"] = $file; // Get avatar icons if ($icons == 'yes') { if (preg_match('/^@([a-zA-Z0-9_@]{1,29}$)/', $read_cmt['name'])) { $avatar = $root_dir . 'scripts/avatars.php?username=' . $read_cmt['name'] . '&email=' . md5(strtolower(trim(encrypt($read_cmt['email'])))); } else { if (preg_match('/([twitter.com|identi.ca]\/[a-zA-Z0-9_@]{1,29}$)/i', $read_cmt['website'])) { $web_username = (preg_match('/twitter.com/i', $read_cmt['website'])) ? preg_replace('/(.*?twitter\.com)\/([a-zA-Z0-9_]{1,20}$)/i', '\\2', $read_cmt['website']) : preg_replace('/(.*?identi\.ca)\/([a-zA-Z0-9_]{1,20}$)/i', '\\2', $read_cmt['website']) . '@identica'; $avatar = $root_dir . 'scripts/avatars.php?username=@' . $web_username . '&email=' . md5(strtolower(trim(encrypt($read_cmt['email'])))); } else { // Get user's Gravatar icon from gravatar.com if (!empty($read_cmt['email'])) { $avatar = 'http://gravatar.com/avatar/' . md5(strtolower(trim(encrypt($read_cmt['email'])))) . '.png?d=http://' . $domain . str_replace('.', '', $root_dir) . 'images/avatar.png&s=' . $icon_size . '&r=pg'; } else { $avatar = $root_dir . 'images/avatar.png'; } } } $html_output = str_ireplace('HOFILL_AVATAR', '#' . $permatext . '', $html_output); } else { $html_output = str_ireplace('HOFILL_AVATAR', '#' . $permatext . '', $html_output); } $name_at = (preg_match('/^@.*?$/', $read_cmt['name'])) ? '@' : ''; $name_class = (preg_match('/^@.*?$/', $read_cmt['name'])) ? ' at' : ''; if (empty($read_cmt['website'])) { if (preg_match('/^@([a-zA-Z0-9_@]{1,29}$)/', $read_cmt['name'])) { $variable_name = $name_at . '' . preg_replace('/^@(.*?)$/', '\\1', str_replace('@identica', '@identica', $read_cmt['name'])) . ''; } else { $variable_name = preg_replace('/^@(.*?)$/', '\\1', str_replace('@identica', '@identica', $read_cmt['name'])); } } else { $variable_name = $name_at . '' . preg_replace('/^@(.*?)$/', '\\1', str_replace('@identica', '@identica', $read_cmt['name'])) . ''; } // Format date and time if ($short_dates == 'yes') { $get_cmtdate = explode(" - ", $read_cmt['date']); $make_cmtdate = new DateTime($get_cmtdate[0]); $cur_date = new DateTime(date('m/d/Y')); $interval = $make_cmtdate->diff($cur_date); if ($interval->y != '') { $read_cmt['date'] = $interval->y . ' year'; $read_cmt['date'] .= ($interval->y != '1') ? 's ago' : ' ago'; } else if ($interval->m != '') { $read_cmt['date'] = $interval->m . ' month'; $read_cmt['date'] .= ($interval->m != '1') ? 's ago' : ' ago'; } else if ($interval->d != '') { $read_cmt['date'] = $interval->d . ' day'; $read_cmt['date'] .= ($interval->d != '1') ? 's ago' : ' ago'; } else { $read_cmt['date'] = $get_cmtdate[1] . ' today'; } } // Replace HOFILL_* statements with content $html_output = str_ireplace('HOFILL_PERMALINK', $permalink, $html_output); $html_output = str_ireplace('HOFILL_ACTION', basename(__FILE__), $html_output); $html_output = str_ireplace('HOFILL_NAME', '' . $variable_name . '', $html_output); $html_output = str_ireplace('HOFILL_THREAD', ((preg_match("/r/", $permalink)) ? 'Top of Thread' : ''), $html_output); $html_output = str_ireplace('HOFILL_DATE', '' . $read_cmt['date'] . '', $html_output); $html_output = str_ireplace('HOFILL_LIKES', (($read_cmt['likes'] > '0') ? '· ' . $read_cmt['likes'] . ' Like' . (($read_cmt['likes'] != '1') ? 's' : '') : ''), $html_output); // "Edit" and "Like" cookies $edit_cookie = 'hashover-' . strtolower(str_replace(' ', '-', $read_cmt['name'])); $like_cookie = md5($_SERVER['SERVER_NAME'] . $ref_path . '/' . basename($file, '.xml')); // Setup "Like" link if (!isset($_COOKIE[$like_cookie])) { $like_onclick = 'like(\'' . $permalink . '\', \'' . basename($file, '.xml') . '\'); '; $like_title = '\'Like\' This Comment'; $like_class = 'like'; } else { if ($_COOKIE[$like_cookie] == 'liked') { $like_onclick = 'like(\'' . $permalink . '\', \'' . basename($file, '.xml') . '\'); '; $like_title = 'You \'Liked\' This Comment'; $like_class = 'liked'; } else { $like_onclick = 'like(\'' . $permalink . '\', \'' . basename($file, '.xml') . '\'); '; $like_title = '\'Like\' This Comment'; $like_class = 'like'; } } // Define "Like" link for everyone except original poster if (!isset($_COOKIE[$edit_cookie]) or $_COOKIE[$edit_cookie] != hash('ripemd160', $read_cmt['email'] . $read_cmt['passwd'])) { if (!isset($_COOKIE['email']) or encrypt($_COOKIE['email']) != $read_cmt['email']) { $html_output = str_ireplace('HOFILL_LIKE', 'Like', $html_output); } } // Define "Edit" link if proper login cookie set if (isset($_COOKIE[$edit_cookie]) and $_COOKIE[$edit_cookie] == hash('ripemd160', $read_cmt['email'] . $read_cmt['passwd'])) { $html_output = str_ireplace('HOFILL_EDIT', ((!empty($read_cmt['passwd'])) ? 'Edit' : ''), $html_output); } // Define "Reply" link with appropriate tooltip if (!empty($read_cmt['email'])) { if (isset($_COOKIE['email']) and encrypt($_COOKIE['email']) == $read_cmt['email']) { $email_indicator = 'You will not be notified via e-mail" class="no-email"'; } else{ $email_indicator = $read_cmt['name'] . ' will be notified via e-mail" class="has-email"'; } } else { $email_indicator = $read_cmt['name'] . ' is not subscribed to e-mail notifications" class="no-email"'; } $html_output = str_ireplace('HOFILL_REPLY', '\n' : '') . '
\n'); foreach (explode(PHP_EOL, $html_output) as $line) $variable .= (preg_match('/[a-z0-9]/i', $line)) ? jsAddSlashes($line . '\n') : ''; $variable .= jsAddSlashes('
\n') . PHP_EOL; } else { if ($icons == 'yes') { $icon_fmt = '#' . $permatext . ''; } else { $icon_fmt = '#' . $permatext . ' '; } $variable .= jsAddSlashes('' . $icon_fmt . ''); $variable .= jsAddSlashes('' . $del_note . '\n'); $variable .= jsAddSlashes('\n') . PHP_EOL; } } return $variable; } $subfile_count = array(); // Individual comment thread count $cmt_count = '1'; // Comment count excluding replies $total_count = '1'; // Comment count including replies $show_cmt = ''; // Will contain all comments // Function for adding deletion notice to output function deletion_notice($file, $check) { global $dir, $show_cmt, $indentation, $icon_fmt, $root_dir, $del_note, $subfile_count, $total_count, $cmt_count, $icons, $icon_size; // Check whether to generate output if (!empty($check) and $check == 'yes' or $check == 'true') { // Generate permalink $del_permatext = (end(explode('-', basename($file, '.xml'))) - 1); if (preg_match('/-/', basename($file, '.xml'))) { $del_permalink = 'c' . str_replace('-', 'r', basename($file, '.xml')); $del_permalink = basename($del_permalink, end(explode('r', $del_permalink))) . (end(explode('r', $del_permalink)) - 1); } else { $del_permalink = 'c' . (basename($file, '.xml') - 1); } // Calculate CSS padding for reply indentation if (($del_dashes = substr_count(basename($file), '-')) != '0' and $check == 'yes') { $del_indent = ($icon_size + 20) * $del_dashes; } else { $del_indent = '0'; } $del_indent_side = ($indentation == 'right') ? '16px ' . $del_indent . 'px 12px 0px' : '16px 0px 12px ' . $del_indent . 'px'; $show_cmt .= jsAddSlashes('\n'); $show_cmt .= jsAddSlashes('
\n'); if ($icons == 'yes') { $icon_fmt = '#' . $del_permatext . ''; } else { $icon_fmt = '#' . $del_permatext . ' '; } $show_cmt .= jsAddSlashes('' . $icon_fmt . ''); $show_cmt .= jsAddSlashes('
\n'); $show_cmt .= jsAddSlashes('' . $del_note . '\n'); $show_cmt .= jsAddSlashes('
\n
\n') . PHP_EOL; } // Count deleted comment if (preg_match('/-/', basename($file, '.xml'))) { $thread = $dir . '/' . basename($file, '-' . end(explode('-', basename($file)))) . '.xml'; $subfile_count["$thread"]++; } else { $total_count++; $cmt_count++; } } $deleted_files = array(); // Deleted files $prev_file = '1'; // Previously read comment // Read, count, and create deleted comment note function read_comments($dir, $check) { global $root_dir, $show_cmt, $cmt_count, $total_count, $subfile_count, $deleted_files, $prev_file, $domain, $script_query, $template, $icon_size; // Read directory contents $dir_iter = new RecursiveDirectoryIterator($dir); $iter = new RecursiveIteratorIterator($dir_iter, RecursiveIteratorIterator::SELF_FIRST); $files = array(); // Put filenames in array, count files foreach ($iter as $file) { if (is_file($file)) { $files[basename($file->getPathname(), '.xml')] = $file; $subfile_count[$file->getPathname()] = '0'; $total_count++; if (!preg_match('/-/', basename($file, '.xml'))) { $cmt_count++; } } } uksort($files, 'strnatcasecmp'); // Sort files ascending alphabetically // Check whether to generate HTML or RSS output if (!isset($_GET['rss']) or !isset($script_query)) { foreach ($files as $path => $file) { if (preg_match('/-/', basename($file, '.xml'))) { $first_file = $dir . '/' . basename($file, end(explode('-', $file))) . '1.xml'; if (substr_count($prev_file, '-') > substr_count(basename($file, '.xml'), '-')) { $reply_levels = substr_count($prev_file, '-') - (substr_count($prev_file, '-') - substr_count($file, '-')); for ($r = '0'; $r < $reply_levels; $r++) { $prev_file = preg_replace('/^(.*)-.*$/', '\\1', $prev_file); } } } else { $prev_file = preg_replace('/-.*/', '', $prev_file); $first_file = $dir . '/' . '1.xml'; } // Check if first comment exists, display notice if (!in_array($first_file, $deleted_files) and !file_exists($first_file)) { deletion_notice($file, $check); $deleted_files[] .= $first_file; } // Check for deleted comment(s), display notice while (end(explode('-', basename($file, '.xml'))) > (end(explode('-', $prev_file)) + 1)) { deletion_notice($file, $check); // Display notice $prev_file++; } // Check whether to generate output if (!empty($check) and $check == 'yes' or $check == 'true') { $show_cmt = parse_comments($file, $show_cmt, 'yes'); } // Count comment if (preg_match('/-/', basename($file, '.xml'))) { $thread = $dir . '/' . basename($file, '-' . end(explode('-', basename($file)))) . '.xml'; $subfile_count["$thread"]++; } $prev_file = basename($file, '.xml'); // Set last file $subfile_count["$file"]++; // Count comment } } else { // Function for RSS feeds if (!empty($_GET['rss']) and isset($script_query)) { header('Content-Type: application/xml'); // Set feed title if (isset($_GET['title']) and !empty($_GET['title'])) { $title = $_GET['title']; } else { $title = $domain . ': ' . basename($dir); } // Function to read comment directory function rglob($pattern = '*', $flags = 0, $path = '') { $paths = glob($path . '*', GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT); $files = glob($path . $pattern, $flags); foreach ($paths as $path) { $files = array_merge($files, rglob($pattern, $flags, $path)); } return $files; } $files = array(); $ac = 0; // Read comment files into array; convert date into UNIX timestamp foreach (rglob('*.xml', 0, $dir) as $file) { $files[$ac] = simplexml_load_file($file); $files[$ac]['date'] = strtotime(str_replace(array('- ', 'am', 'pm'), array('', ' AM PST', ' PM PST'), $files[$ac]['date'])); $files[$ac]['file'] = $file; $ac++; } // Sort by comment creation date usort($files, function ($a, $b) { return ($b['date'] - $a['date']); }); foreach ($files as $rss_cmt) { static $rss_feed = ''; // Set feed title if (isset($_GET['title']) and !empty($_GET['title'])) { $title = $_GET['title']; } else { preg_match('/(.+)<\/title>/', file_get_contents($_GET['rss']), $matches); if (!empty($matches[1])) { header('Location: .' . $_SERVER['PHP_SELF'] . '?rss=' . $_GET['rss'] . '&title=' . $matches[1]); } else { $title = str_replace('www.', '', $domain) . ': ' . basename($dir); } } // Error handling if ($rss_cmt !== false) { $data_search = array('&', '<br>\n', '\n', '\r', ' '); $data_replace = array('&', ' ', ' ', ' ', ' '); $permalink = 'c' . str_replace('-', 'r', basename($rss_cmt['file'], '.xml')); $rss_feed .= "\t\t" . '<item>' . PHP_EOL; $rss_feed .= "\t\t\t" . '<title>' . str_replace('@identica', '', $rss_cmt['name']) . ': '; $rss_feed .= (strlen($rss_cmt->body) > 40) ? substr(str_replace(array('\n', '\r'), ' ', htmlspecialchars(strip_tags($rss_cmt->body))), 0, 40) . '...' . PHP_EOL : str_replace(array('\n', '\r'), ' ', htmlspecialchars(strip_tags($rss_cmt->body))) . '' . PHP_EOL; $rss_feed .= "\t\t\t" . '' . str_replace('@identica', '', $rss_cmt['name']) . '<hr size="1">' . str_replace(array('\n', '\r'), '', htmlspecialchars(strip_tags($rss_cmt->body, '
'))) . '' . PHP_EOL; // Added avatar URLs to feed if (preg_match('/^@([a-zA-Z0-9_@]{1,29}$)/', $rss_cmt['name'])) { $rss_avatar = 'http://' . $domain . str_replace('.', '', $root_dir) . 'scripts/avatars.php?username=' . $rss_cmt['name'] . '&email=' . md5(strtolower(trim(encrypt($rss_cmt['email'])))); } else { if (preg_match('/([twitter.com|identi.ca]\/[a-zA-Z0-9_@]{1,29}$)/i', $rss_cmt['website'])) { $web_username = (preg_match('/twitter.com/i', $rss_cmt['website'])) ? preg_replace('/(.*?twitter\.com)\/([a-zA-Z0-9_]{1,20}$)/i', '\\2', $rss_cmt['website']) : preg_replace('/(.*?identi\.ca)\/([a-zA-Z0-9_]{1,20}$)/i', '\\2', $rss_cmt['website']) . '@identica'; $rss_avatar = 'http://' . $domain . str_replace('.', '', $root_dir) . 'scripts/avatars.php?username=@' . $web_username . '&email=' . md5(strtolower(trim(encrypt($rss_cmt['email'])))); } else { // Get user's Gravatar icon from gravatar.com if (!empty($rss_cmt['email'])) { $rss_avatar = 'http://gravatar.com/avatar/' . md5(strtolower(trim(encrypt($rss_cmt['email'])))) . '.png?d=http://' . $domain . str_replace('.', '', $root_dir) . 'images/avatar.png&s=' . $icon_size . '&r=pg'; } else { $rss_avatar = 'http://' . $domain . str_replace('.', '', $root_dir) . 'images/avatar.png'; } } } $rss_feed .= "\t\t\t" . '' . $rss_avatar . '' . PHP_EOL; $rss_feed .= "\t\t\t" . '' . date('D, d M Y H:i:s O', (string)$rss_cmt['date']) . '' . PHP_EOL; $rss_feed .= "\t\t\t" . '' . $_GET['rss'] . '#' . $permalink . '' . PHP_EOL; $rss_feed .= "\t\t\t" . '' . $_GET['rss'] . '#' . $permalink . '' . PHP_EOL; $rss_feed .= "\t\t" . '' . PHP_EOL; } else { $total_count--; } } echo '' . PHP_EOL; echo '' . PHP_EOL; echo "\t" . '' . PHP_EOL; echo "\t\t" . '' . $title . '' . PHP_EOL; echo "\t\t" . '' . $_GET['rss'] . '' . PHP_EOL; echo "\t\t" . 'Displaying ' . ($total_count - 1) . ' Comments' . PHP_EOL; echo "\t\t" . '' . PHP_EOL; echo "\t\t" . 'en-us' . PHP_EOL; echo "\t\t" . '40' . PHP_EOL; echo utf8_encode($rss_feed); echo "\t" . '' . PHP_EOL; exit(''); } } } // Function for displaying comment count function display_count() { global $cmt_count, $total_count; $cmt_count--; $total_count--; if ($total_count == $cmt_count) { $show_count = $cmt_count . ' Comment'; if ($cmt_count != '1') $show_count .= 's'; } else { $show_count = $cmt_count . ' Comment'; if ($cmt_count != '1') $show_count .= 's'; $show_count .= ' (' . $total_count . ' counting repl'; $show_count .= ($total_count != '2') ? 'ies)' : 'y)'; } return $show_count; } // If the "count_link" query is set, echo comment count as link if (isset($script_query)) { if (isset($_GET['count_link']) and !empty($_GET['count_link'])) { read_comments($dir, 'no'); // Run read_comments function if ($total_count > 1) { exit(jsAddSlashes('' . display_count() . '', 'single')); } else { exit(jsAddSlashes('Post Comment', 'single')); } } } // Characters to be removed from name, email, and website fields $search = array('<', '>', "\n", "\r", "\t", ' ', '<', '>', '"', "'", '\\', '_'); $replace = array('', '', '', '', '', '', '', '', '"', ''', '', '_'); $header = "From: $enotify\n\nReply-To: $enotify"; // Mail headers when no email is given // Remove any HOFILL_* statements from POST requests if (isset($_POST)) { foreach ($_POST as $post_name=>$post) { $_POST[$post_name] = preg_replace('/HOFILL_([a-z0-9-]+)/i', '\\1', $post); } } // Clean up name, set name cookie if (isset($_POST['name']) and trim($_POST['name'], ' ') != '' and $_POST['name'] != 'Nickname or @name') { $name = substr(ucwords(strtolower(str_replace($search, $replace, $_POST['name']))), 0, 30); setcookie('name', $name, $expire, '/', str_replace('www.', '', $domain)); } // Set password cookie if (isset($_POST['password']) and (!empty($_POST['password']) and $_POST['password'] != 'Password')) { if (!isset($_POST['edit']) and !isset($_POST['delete'])) { setcookie('password', str_replace('"', '"', stripslashes($_POST['password'])), $expire, '/', str_replace('www.', '', $domain)); } } // Clean up email, set email cookie if (isset($_POST['email']) and trim($_POST['email'], ' ') != '' and $_POST['email'] != 'E-mail Address') { $email = str_replace($search, '', $_POST['email']); $header = (trim($_POST['email'], ' ') != '') ? "From: $email\r\nReply-To: $email" : $header; setcookie('email', $email, $expire, '/', str_replace('www.', '', $domain)); } // Clean up web address, set website cookie if (isset($_POST['website']) and trim($_POST['website'], ' ') != '' and $_POST['website'] != 'Website') { $website = str_replace($search, $replace, $_POST['website']); $website = (!preg_match('/htt[p|ps]:\/\//i', $website)) ? 'http://' . $website : $website; setcookie('website', $website, $expire, '/', str_replace('www.', '', $domain)); } function sanitize($query) { $value = str_replace('../', '', $query); return $value; } // Delete comment if (isset($_POST['delete']) and (isset($_POST['password']) and !empty($_POST['password']))) { if (file_exists($dir . '/' . $_POST['cmtfile'] . '.xml')) { $del_file = $dir . '/' . sanitize($_POST['cmtfile'] . '.xml'); $get_pass = simplexml_load_file($del_file); } else { header('Location: ' . $_SERVER['HTTP_REFERER'] . '#comments'); exit; } // Check if password matches the one in the file if (md5(encrypt(stripslashes($_POST['password']))) == $get_pass['passwd']) { unlink($del_file); // Delete the comment file setcookie('password', str_replace('"', '"', $_POST['password']), $expire, '/', str_replace('www.', '', $domain)); read_comments($dir, 'no'); // Read comments without output // Kick visitor back to comment if (file_exists($dir . '/' . ($_POST['cmtfile'] + 1) . '.xml')) { header('Location: ' . $_SERVER['HTTP_REFERER'] . '#c' . str_replace('-', 'r', $_POST['cmtfile'])); exit; } else { header('Location: ' . $_SERVER['HTTP_REFERER'] . '#comments'); exit; } } else { header('Location: ' . $_SERVER['HTTP_REFERER'] . '#c' . str_replace('-', 'r', $_POST['cmtfile'])); exit; } } // Check if a comment has been entered, clean comment, replace HTML, create hyperlinks if ((isset($_POST['comment']) and !empty($_POST['comment'])) and !isset($_POST['delete'])) { if (trim($_POST['comment'], " \r\n") != '' and (!preg_match('/' . str_replace(array('(', ')'), array('\(', '\)'), $comment_form) . '/i', $_POST['comment']) and !preg_match('/' . str_replace(array('(', ')'), array('\(', '\)'), $reply_form) . '/i', $_POST['comment']))) { // Characters to search for and replace with in comments $data_search = array('\\', '"', '<', '>', "\n\r", "\n", "\r", ' ', '<b>', '</b>', '<u>', '</u>', '<i>', '</i>', '<s>', '</s>', '<pre>', '</pre>', '<code>', '</code>', '<ul>', '</ul>', '<ol>', '</ol>', '<li>', '</li>', '<blockquote>', '</blockquote>'); $data_replace = array('\', '"', '<', '>', '
', '', '
', '  ', '', '', '', '', '', '', '', '', '
', '
', '', '', '
    ', '
', '
    ', '
', '
  • ', '
  • ', '
    ', '
    '); $clean_code = preg_replace('/(((ftp|http|https){1}:\/\/)[a-zA-Z0-9-@:%_\+.~#?&\/=]+)/i', '\\1 ', $_POST['comment']); // Add space to end of URLs to separate '&' characters from escaped HTML tags $clean_code = str_ireplace($data_search, $data_replace, preg_replace('/\n{2,}/', "\n\r\n", preg_replace('/^\s+$/m', '', rtrim($clean_code, " \r\n")))); // Escape HTML tags; remove trailing new lines $clean_code = preg_replace('/(((ftp|http|https){1}:\/\/)[a-zA-Z0-9-@:%_\+.~#?&\/=]+)([\s]{0,})/i', '\\1', $clean_code); // Add HTML anchor tag to URLs $clean_code = preg_replace_callback('/\[img\](((ftp|http|https){1}:\/\/)[a-zA-Z0-9-@:%_\+.~#?&\/=]+)<\/a>\[\/img\]/i', function($arr) { return ((in_array(pathinfo($arr[1], PATHINFO_EXTENSION), array('jpeg', 'jpg', 'png', 'gif'))) ? '

    Loading...

    ' : '' . $arr[1] . ''); }, $clean_code); // Replace [img] tags with external image placeholder $clean_code = preg_replace('/^(

    )/', '', preg_replace('/(

    )$/', '', preg_replace('/(
    ){2,}/i', '

    ', $clean_code))); // Remove repetitive and trailing HTML
    tags // HTML tags to automatically close $tags = array('code', 'b', 'i', 'u', 's', 'li', 'pre', 'blockquote', 'ul', 'ol'); $cleantags = array('blockquote', 'ul', 'ol'); // Check if all allowed HTML tags have been closed, if not add them at the end for ($tc = '0'; $tc != count($tags); $tc++) { $open_tags = substr_count(strtolower($clean_code), '<' . $tags[$tc] . '>'); $close_tags = substr_count(strtolower($clean_code), ''); if ($open_tags != $close_tags) { while ($open_tags > $close_tags) { $clean_code .= ''; $close_tags++; } while ($close_tags > $open_tags) { $clean_code = preg_replace('/' . str_replace('/', '\/', '') . '/i', '', $clean_code, 1); $close_tags--; } } if (in_array($tags[$tc], $cleantags)) { $clean_code = str_ireplace(array('<' . $tags[$tc] . '>
    ', '
    '), array('<' . $tags[$tc] . '>\n', '\n'), $clean_code); } } $clean_code = str_ireplace(array('
    ', '
    '), array('', ''), $clean_code); $clean_code = str_ireplace(array('

    ', '
    '), array('
    ', '
    '), $clean_code); $clean_code = preg_replace_callback('/()(.*?)(<\/code>){1,}/i', function($arr) { return '' . str_ireplace('<br>', '
    ', htmlspecialchars(preg_replace('/(
    ){1,}(
    ){1,}/', '\\2', preg_replace('/<\/?a(\s+.*?>|>)/', '', $arr[2])), null, null, false)) . $arr[3];}, $clean_code); $clean_code = preg_replace_callback('/(
    )(.*?)(<\/pre>){1,}/i', function($arr) { return $arr[1] . preg_replace('/(
    ){1,}(
    ){1,}/', '\\2', $arr[2]) . $arr[3];}, $clean_code); $clean_code = str_replace(array('
    \n
    ', '

    '), array('
    \n', '\n
    '), $clean_code); $clean_code = str_ireplace('
    ', '\n', $clean_code); // Open comment template; prepare data $write_cmt = simplexml_load_file('template.xml'); $write_cmt['name'] = trim($name, ' '); $write_cmt['email'] = (isset($_POST['email']) and $_POST['email'] != 'E-mail Address') ? str_replace('"', '"', encrypt(stripslashes($_POST['email']))) : ''; $write_cmt['passwd'] = (isset($_POST['password']) and !empty($_POST['password']) and $_POST['password'] != 'Password') ? md5(encrypt(stripslashes($_POST['password']))) : ''; $write_cmt['website'] = (isset($website)) ? trim($website, ' ') : ''; $write_cmt['date'] = date('m/d/Y - g:ia'); $write_cmt['ipaddr'] = ($ip_addrs == 'yes') ? $_SERVER['REMOTE_ADDR'] : ''; $write_cmt['likes'] = '0'; $write_cmt->body = $clean_code; // Final comment body // Edit comment if (isset($_POST['edit']) and (isset($_POST['password']) and isset($_POST['cmtfile'])) and !isset($_POST['delete'])) { if (file_exists($dir . '/' . $_POST['cmtfile'] . '.xml')) { $edit_cmt = simplexml_load_file($dir . '/' . sanitize($_POST['cmtfile'] . '.xml')); // Check if password matches the one in the file if (md5(encrypt(stripslashes($_POST['password']))) == $edit_cmt['passwd']) { // Write edited comment to file $edit_cmt['name'] = $write_cmt['name']; $edit_cmt['email'] = $write_cmt['email']; $edit_cmt['website'] = $write_cmt['website']; if ($clean_code != $edit_cmt->body) $edit_cmt->body = $clean_code; $edit_cmt->asXML($dir . '/' . sanitize($_POST['cmtfile']) . '.xml'); // Set "Password" and "Login" cookies setcookie('password', str_replace('"', '"', stripslashes($_POST['password'])), $expire, '/', str_replace('www.', '', $domain)); setcookie('hashover-' . strtolower(str_replace(' ', '-', $name)), hash('ripemd160', $write_cmt['email'] . $write_cmt['passwd']), $expire, '/', str_replace('www.', '', $domain)); } } // Kick visitor back to comment header('Location: ' . $_SERVER['HTTP_REFERER'] . '#c' . str_replace('-', 'r', $_POST['cmtfile'])); exit; } read_comments($dir, 'no'); // Read comments without output // Rename file for reply if (isset($_POST['reply_to']) and !empty($_POST['reply_to'])) { if (!preg_match('/[a-zA-Z]/i', $_POST['reply_to']) and file_exists($dir . '/' . $_POST['reply_to'] . ".xml")) { // Set reply directory information & "cookie" for successful reply $reply_dir = $dir . '/' . $_POST['reply_to'] . '.xml'; $cmt_file = $dir . '/' . $_POST['reply_to'] . '-' . $subfile_count["$reply_dir"] . '.xml'; setcookie('replied', $_POST['reply_to'], $expire, '/', str_replace('www.', '', $domain)); } } else { $cmt_file = $dir . '/' . $cmt_count . '.xml'; } // Write comment to file if ($write_cmt->asXML(sanitize($cmt_file))) { chmod($cmt_file, 0711); // Send notification e-mails $permalink = 'c' . str_replace('-', 'r', basename($cmt_file, '.xml')); $from_email = (isset($_POST['email']) and $_POST['email'] != 'E-mail Address') ? $name . ' <' . $_POST['email'] . '>' : $name; $reverse_datasearch = array('"', '<', '>', '
    \n', '\n', '
    ', ' ', '\r'); $reverse_datareplace = array('"', '<', '>', PHP_EOL, PHP_EOL, "\r", ' ', "\r"); $to_webmaster = ''; // Notify commenter of reply if (isset($_POST['reply_to']) and !empty($_POST['reply_to'])) { if (!preg_match('/[a-zA-Z]/i', $_POST['reply_to']) and file_exists($dir . '/' . $_POST['reply_to'] . ".xml")) { $get_cmt = simplexml_load_file($dir . '/' . sanitize($_POST['reply_to'] . '.xml')); $to_commenter = "\nIn reply to:\n\n\t" . str_replace($reverse_datasearch, $reverse_datareplace, strip_tags($get_cmt->body)) . "\n\n"; $to_webmaster = "\nIn reply to " . $get_cmt['name'] . ":\n\n\t" . str_replace($reverse_datasearch, $reverse_datareplace, strip_tags($get_cmt->body)) . "\n\n"; $decryto = encrypt($get_cmt['email']); if (!empty($decryto) and $decryto != $enotify and $decryto != $_POST['email']) { mail($decryto, $_SERVER['HTTP_HOST'] . ' - New Reply', "From $name:\n\n\t" . strip_tags(stripslashes($_POST['comment'])) . "\n\n$to_commenter----\nPermalink: $page_url" . '#' . $permalink . "\nPage: $page_url", $header); } } } // Notify webmaster via e-mail if ($write_cmt['email'] != encrypt($enotify)) { mail($enotify, 'New Comment', "From $from_email:\n\n\t" . str_replace($reverse_datasearch, $reverse_datareplace, strip_tags($clean_code)) . "\n\n$to_webmaster----\nPermalink: $page_url" . '#' . $permalink . "\nPage: $page_url", $header); } // Set blank cookie for successful comment, kick visitor back to comment setcookie('replied', '', $expire, '/', str_replace('www.', '', $domain)); setcookie('hashover-' . strtolower(str_replace(' ', '-', $name)), hash('ripemd160', $write_cmt['email'] . $write_cmt['passwd']), $expire, '/', str_replace('www.', '', $domain)); header('Location: ' . $_SERVER['HTTP_REFERER'] . '#' . $permalink); exit; } else { header('Location: ' . $_SERVER['HTTP_REFERER'] . '#comments'); exit; } } else { setcookie('success', 'no', $expire, '/', str_replace('www.', '', $domain)); // Set failed comment cookie if (isset($_POST['reply_to']) and !empty($_POST['reply_to'])) setcookie('replied', $_POST['reply_to'], $expire, '/', str_replace('www.', '', $domain)); header('Location: ' . $_SERVER['HTTP_REFERER'] . '#comments'); exit; // Kick visitor back to comment form } } // Check if either a comment or reply failed to post if (isset($_COOKIE['success']) and $_COOKIE['success'] == 'no') { if (!isset($_COOKIE['replied']) and empty($_COOKIE['replied'])) { setcookie('success', '', $expire, '/', str_replace('www.', '', $domain)); } else { $comment_form = $reply_form; $post_button = 'Post Reply'; setcookie('replied', '', $expire, '/', str_replace('www.', '', $domain)); setcookie('success', '', $expire, '/', str_replace('www.', '', $domain)); } } // Change document type to match following output if ($mode == 'javascript') { header('Content-Type: text/javascript'); } else { echo '' . "\n\n" : PHP_EOL; if ($page_title = 'yes') { $js_title = ($mode == 'javascript') ? "'+pagetitle+'" : ''; $js_title = (isset($_GET['pagetitle'])) ? ' on "' . $_GET['pagetitle'] . '"' : $js_title; $rss_title = (isset($_GET['pagetitle'])) ? $_GET['pagetitle'] : "'+document.title+'"; } if ($mode == 'php') { echo '
    ' . PHP_EOL; } else { echo '// Place "hashover" DIV' . PHP_EOL; echo 'if (document.getElementById("hashover") == null) {' . PHP_EOL; echo "\t" . 'document.write("
    \n");' . PHP_EOL; echo '}' . PHP_EOL . PHP_EOL; } $tab = ($mode == 'javascript') ? "\t" : ''; $end_curly = ($mode == 'javascript') ? "}\n\n" : ''; echo jsAddSlashes('
    Post a Comment' . $js_title . ':

    \n'); echo jsAddSlashes('
    \n'); echo jsAddSlashes('\n\n\n'); if ($mode == 'javascript') echo "\nif (email_on == 'yes') {\n"; echo $tab . jsAddSlashes('\n'); echo $end_curly; // Display name input tag if told to if ($mode == 'javascript') echo "\nif (name_on == 'yes') {\n"; echo $tab . jsAddSlashes('\n'); echo $end_curly; // Display password input tag if told to if ($mode == 'javascript') echo "if (passwd_on == 'yes') {\n"; echo $tab . jsAddSlashes('\n'); echo $end_curly; // Add second table row on mobile devices echo ($is_mobile == 'yes') ? jsAddSlashes('\n\n') : ''; // Display email input tag if told to if ($mode == 'javascript') echo "if (email_on == 'yes') {\n"; echo $tab . jsAddSlashes('\n'); echo $end_curly; // Display website input tag if told to if ($mode == 'javascript') echo "if (sites_on == 'yes') {\n"; echo $tab . jsAddSlashes('\n'); echo $end_curly; echo jsAddSlashes('\n\n
    \n'); if (isset($_COOKIE['name']) and preg_match('/^@([a-zA-Z0-9_@]{1,29}$)/', $_COOKIE['name'])) { echo $tab . jsAddSlashes('\n'); } else { echo $tab . jsAddSlashes('\n' : $root_dir . 'images/avatar.png">\n'); } echo $tab . jsAddSlashes('\n'); echo $tab . jsAddSlashes('\n' : 'Nickname or @name">\n'); echo $tab . jsAddSlashes('\n'); echo $tab . jsAddSlashes('\n' : 'text" value="Password">\n'); echo $tab . jsAddSlashes('
    \n'); echo $tab . jsAddSlashes('\n' : 'E-mail Address">\n'); echo $tab . jsAddSlashes('\n'); echo $tab . jsAddSlashes('\n' : 'Website">\n'); echo $tab . jsAddSlashes('
    \n') . PHP_EOL; $rows = ($mode == 'javascript') ? "'+rows+'" : $rows; $replyborder = (isset($_COOKIE['success']) and $_COOKIE['success'] == "no") ? ' border: 2px solid #FF0000 !important; -moz-border-radius: 5px 5px 0px 0px; border-radius: 5px 5px 0px 0px;' : ''; echo jsAddSlashes('
    \n'); echo jsAddSlashes('
    \n'); echo jsAddSlashes('
    \n'); echo (isset($_GET['canon_url']) or isset($canon_url)) ? jsAddSlashes('\n') : ''; echo (isset($_COOKIE['replied'])) ? jsAddSlashes('\n') : ''; echo jsAddSlashes('
    \n

    \n'). PHP_EOL; // Display three most popular comments if (!empty($top_likes)) { $ts = (count($top_likes) != '1') ? 's' : ''; echo jsAddSlashes('
    Most Popular Comment' . $ts . ':\n') . PHP_EOL; for ($p = 0; $p != $top_cmts; $p++) { if (!empty($top_likes)) { echo parse_comments(array_shift($top_likes), '', 'no'); } } echo jsAddSlashes('
    ') . PHP_EOL; } // Display comment count echo jsAddSlashes('
    Showing ' . $script = ($cmt_count == "1") ? '0 Comments:' : display_count() . ':'); // Display comments, if there are no comments display a note if (!empty($show_cmt)) { echo $show_cmt; } else { echo jsAddSlashes('
    \n'); echo jsAddSlashes('\n'); echo jsAddSlashes('
    \n'); echo jsAddSlashes('Be the first to comment!'); echo jsAddSlashes('
    \n
    \n') . PHP_EOL; } echo jsAddSlashes('
    \n'); echo jsAddSlashes('HashOver Comments ·\n'); if ($cmt_count != '1') { if ($mode == 'javascript') { echo jsAddSlashes('RSS Feed ·\n'); } else { echo jsAddSlashes('RSS Feed ·\n'); } } // Script execution ending time $exec_time = explode(' ', microtime()); $exec_end = $exec_time[1] + $exec_time[0]; $exec_time = ($exec_end - $exec_start); echo jsAddSlashes('Source Code ·\n'); echo ($mode == 'javascript') ? jsAddSlashes('JavaScript ·\n') : ''; echo jsAddSlashes('ChangeLog ·\n'); echo jsAddSlashes('Archives
    \n'); echo jsAddSlashes('
    \n'); if ($mode == 'php') { echo '
    '; } else { echo PHP_EOL . '// Place all content on page' . PHP_EOL; echo 'document.getElementById("hashover").innerHTML = show_cmt;' . PHP_EOL . PHP_EOL; echo '// Script Execution Time: ' . round($exec_time, 5) . ' Seconds'; } ?>