Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 3544 lines (2792 sloc) 134 KB
<?
function real_if_private($post_id, $user_id) { // if a post is private, and user_id's user_realname exists, return the user_realname
global $db;
$u = get_userrow($user_id);
if (get_post($post_id)->post_private) return $u->user_realname ? $u->user_realname : $u->user_name;
else return $u->user_name;
}
function trustworthy_client_ip() {
global $db;
$ip = $_SERVER['REMOTE_ADDR'];
if ('localdev' == gethostname()) return true;
if ( (ip2country($ip) == 'United States') and
!intval($db->get_var("select count(*) from users where user_last_comment_ip='$ip'")) // has not commented before from this ip
) return true;
return false;
}
function username_availability_js() {
// used for user_name, email for registration, post-registration, and comment-registration
?>
<script type="text/javascript">
function pullAjax() { // create ajax object for various browsers
var a;
try {
a=new XMLHttpRequest()
}
catch(b) {
try {
a=new ActiveXObject("Msxml2.XMLHTTP")
} catch(b) {
try {
a=new ActiveXObject("Microsoft.XMLHTTP")
}
catch(b) {
console.log("could not create ajax object"); return false
}
}
}
return a;
}
function check_username() {
var msg = document.getElementById('msg');
var x = document.getElementById('uname');
user = x.value;
code = '';
message = '';
obj=pullAjax();
obj.onreadystatechange=function() {
if(obj.readyState==4) {
console.log(obj.responseText);
result = JSON.parse(obj.responseText);
code = result['code'];
message = result['result'];
if (code <= 0) {
x.style.border = "1px solid red";
msg.style.color = "red";
}
else {
x.style.border = "1px solid #000";
msg.style.color = "green";
}
msg.innerHTML = message;
}
}
obj.open("GET", "/username_availability.php?user_name="+user, true);
obj.send(null);
}
</script>
<div class='form-group'>
<div id="msg"></div>
<input name='user_name' type='text' placeholder='choose user_name' class='form-control' id='uname' onkeyup="check_username();" >
</div>
<?
}
function page() { // tell homepage, search, userpage, topic which page we are on
$curpage = intval($_GET['page']) ? intval($_GET['page']) : 1;
$slimit = ($curpage - 1) * 20 . ", 20"; // sql limit for pagination of results.
$orders = array ( // maps $_GET['order'] parm to a posts table column name to order by
'active' => 'post_modified',
'comments' => 'post_comments',
'new' => 'post_date',
'likes' => 'cast(post_likes as signed) - cast(post_dislikes as signed)',
);
$default = 'active';
$order = $orders[$_GET['order']] ? $order = $_GET['order'] : $order = $default;
$order_by = 'order by ' . $orders[$order] . ' desc';
return array ($curpage, $slimit, $order, $order_by);
}
function pagination_links($post_count, $curpage, $extra='') {
$pages = intval( ($post_count + 20) / 20);
$nextpage = $curpage + 1;
$prevpage = $curpage - 1;
$uri_parts = explode('?', $_SERVER['REQUEST_URI'], 2);
$path = $uri_parts[0]; // url path without ? parms
if ($curpage > 1) print "<a href='$path?page=$prevpage$extra'>&laquo; previous</a> &nbsp;";
print " page $curpage of $pages ";
if ($curpage < $pages) print "&nbsp; <a href='$path?page=$nextpage$extra'>next &raquo;</a>";
}
function tabs($order, $extra='') {
$selected_tab[$order] = 'class="active"';
$uri_parts = explode('?', $_SERVER['REQUEST_URI'], 2);
$path = $uri_parts[0]; // url path without ? parms
?>
<ul class="nav nav-tabs">
<li <?= $selected_tab['active'] ?> > <a href="<?= $path ?>?order=active<?= $extra ?>" title="most recent comments" >active</a></li>
<li <?= $selected_tab['comments'] ?> > <a href="<?= $path ?>?order=comments<?= $extra ?>" title="most comments in last 7 days" >comments</a></li>
<li <?= $selected_tab['likes'] ?> > <a href="<?= $path ?>?order=likes<?= $extra ?>" title="most liked in last 7 days" >likes</a></li>
<li <?= $selected_tab['new'] ?> > <a href="<?= $path ?>?order=new<?= $extra ?>" title="newest" >new</a></li>
</ul>
<?
}
function get_like_dislike($post) {
global $db, $user_id;
$net = intval($post->post_likes) - intval($post->post_dislikes);
if ( is_user_logged_in() ) {
$votes = $db->get_row("select * from postvotes where postvote_user_id=$user_id and postvote_post_id=$post->post_id");
intval($votes->postvote_up) ? $upgrey = "style='color: grey; pointer-events: none;' title='you liked this' " : "";
intval($votes->postvote_down) ? $downgrey = "style='color: grey; pointer-events: none;' title='you disliked this' " : "";
$likelink = "href='#' $upgrey onclick=\"postlike('post_$post->post_id');return false;\" ";
$dislikelink = "href='#' $downgrey onclick=\"postdislike('post_$post->post_id');return false;\" ";
}
else {
$likelink = "href='/login.php?action=registerform'";
$dislikelink = "href='/login.php?action=registerform'";
}
return "<div class='arrowbox' ><a $likelink >&#9650;</a><br><span id='post_$post->post_id' />$net</span><br><a $dislikelink >&#9660;</a></div>";
}
function get_nonce($ts) {
// create a nonce string for input forms. this makes each form usable only once, and only from the ip that got the form.
// hopefully this slows down spammers and cross-site posting tricks
return md5( $_SERVER['REMOTE_ADDR'] . NONCE_SECRET . $ts );
}
function check_nonce($ts, $nonce) {
if ( md5( $_SERVER['REMOTE_ADDR'] . NONCE_SECRET . $ts ) == $nonce )
return true;
else
return false;
}
function text2hashtag($text) { // given some text, pull out first hashtag
$pattern = '|[\s>]#(\w{1,32})|';
if (preg_match($pattern, $text, $matches)) return $matches[1];
else return '';
}
function unread_comments_icon($post, $i_am_banned_by, $last_view) { // print blinky icon showing that there are unread comments in a post
global $current_user;
// If post_modified > last time they viewed this post, then give them a link to earliest unread comment.
if (strtotime($post->post_modified) > strtotime($last_view[$post->post_id])) {
$since = strtotime($last_view[$post->post_id]);
$unread = " <A HREF='/since.php?p=$post->post_id&since=$since' ><IMG SRC='/content/unread_comments.gif' width='19' height='18' title='View unread comments'></A> &nbsp; ";
return $unread;
}
}
function post_list($posts) {
// format and display a list of posts from whatever source.
// usually from $posts = $db->get_results($sql);
// pass in only a limited number, because all of them will display
if (!$posts) return;
global $current_user, $db;
foreach ($posts as $post) {
$authors[] = $post->post_author;
$pida[] = $post->post_id;
}
$pidl = implode(',', $pida);
$views = $db->get_results("select postview_last_view, postview_post_id from postviews where postview_user_id=$current_user->user_id and postview_post_id in ($pidl)");
foreach ($views as $when) $last_view[$when->postview_post_id] = $when->postview_last_view;
$permissions = $db->get_results("select post_id from whitelists where whitelist_user_id=$current_user->user_id and whitelist_post_id in ($pidl)");
foreach ($permissions as $permit) $allowed[$permit->post_id] = 1;
$i_am_banned_by = banned_by($authors);
foreach ($posts as $post) {
if ($post->post_private and !$allowed[$post->post_id]) continue; // Don't show private post if user is not on its whitelist.
if (!is_user_logged_in() and preg_match('/thunderdome/i', $post->post_title)) continue; // Don't show thunderdome posts to non-logged-in users
if (!is_user_logged_in() and $post->post_nsfw) continue; // Don't show porn posts to non-logged-in users
print "<div class='comment' >";
print get_like_dislike($post);
$unread = ''; // reset for each post
$post_title = $post->post_title;
$post_views = number_format($post->post_views);
$post_author = get_author_name( $post->post_author );
$path = post_id2path($post->post_id);
if ( is_user_logged_in() ) {
if (!$last_view[$post->post_id])
$unread = "<a href='$path' ><IMG SRC='/content/unread_post.gif' width='45' height='16' title='You never read this one' ></a> &nbsp;";
else {
$unread = unread_comments_icon($post, $i_am_banned_by, $last_view);
}
}
$outbound_ref = '';
if ($external_link = get_external_link($post->post_content)) {
$host = parse_url($external_link)['host'];
$host = $host ? $host : 'patrick.net';
$outbound_ref = " <a href='$external_link' target='_blank' title='original story at $host' ><img src='/images/ext_link.png'></a>";
}
if (!$current_user->user_hide_post_list_photos) {
$src = get_first_image($post->post_content);
if ($src) {
if ($post->post_nsfw)
print "<div class='icon' ><a href='$path' ><img src='/images/nsfw.png' border=0 width=100 align=top hspace=5 vspace=5 ></a></div>";
else
print "<div class='icon' ><a href='$path' ><img src='$src' border=0 width=100 align=top hspace=5 vspace=5 ></a></div>";
}
}
if ($post->post_private) $red="color='red'"; else $red="";
print "<a href='$path' ><b><font size='+1' $red >$post_title</font></b></a>$outbound_ref " . share_post($post) . '<br>';
$dt_ts = new DateTime($post->post_date);
$dt_ts->setTimeZone(new DateTimeZone('America/Los_Angeles')); // for now, california time for all users
//$when = $dt_ts->format('D M j, Y');
//$when = rel_time($post->post_date); // override the above line as experiment
if ($tag = text2hashtag($post->post_content)) {
$tlink = " in <a href='/topics/$tag' >#$tag</a>";
if (0 == strlen($post->post_topic)) { // if post_topic not yet set for this post, set it to the tag now
$sql = "update posts set post_topic = '$tag' where post_id=$post->post_id";
$db->query($sql);
}
}
else $tlink = '';
print "by " . name_posts($post->post_author) . $tlink . ' &nbsp; ';
/*
if ($src and !$post->post_nsfw) { // if has img src and not already marked post_nsfw, give the option to do that
$ts = time();
$nonce = get_nonce($ts);
$nonce_parms = "ts=$ts&nonce=$nonce";
$confirm_nsfw = 'onClick="javascript:return confirm(\'Really mark image as porn?\')"';
print " <a href='/nsfw.php?post_id=$post->post_id&$nonce_parms' $confirm_nsfw title='mark as porn image' >nsfw</a> &nbsp; ";
}
*/
$post_comments = number_format(intval($post->post_comments));
$ago = rel_time($post->post_modified);
$s = $post->post_comments == 1 ? "" : "s";
$path = post_id2path($post->post_id);
if ($post->post_comments)
print "<a href='$path'>$post_comments&nbsp;comment$s</a>, latest <a href='$path#comment-$post->post_latest_comment_id' >$ago</a>";
else
print "Posted $ago";
print " $unread <br>";
$content = $post->post_content;
list($content, $more_wc) = first_words( strip_tags($content), 30 );
if ($more_wc) $content .= "... ";
if ($content) print "<font size='-1'>$content</font>";
print "</div>";
}
}
function share_post($post, $style='') {
$share_title = urlencode($post->post_title);
$share_title = str_replace('+', ' ', $share_title);
//$share_title = str_replace('%', ' percent', $share_title); // remove percent
//$share_title = str_replace('\'', '', $share_title); // remove single quotes
$share_link = urlencode('https://patrick.net' . post_id2path($post->post_id) );
return "<a href='mailto:?subject=$share_title&body=$share_link' style='$style' title='email this' ><img src='/images/mailicon.jpg' width=15 height=12 ></a>";
}
function select_my_comment_votes($comments) { // return how the current user voted on a list of comments
global $db, $current_user;
foreach ($comments as $comment) $comment_ids[] = $comment->comment_id;
$comment_idl = implode(',', $comment_ids);
$sql = "select commentvote_comment_id, commentvote_up, commentvote_down from commentvotes where
commentvote_user_id=$current_user->user_id and commentvote_comment_id in ($comment_idl)";
$votes = $db->get_results($sql);
foreach ($votes as $vote) {
$upvotes[ $vote->commentvote_comment_id ] = $vote->commentvote_up;
$downvotes[ $vote->commentvote_comment_id ] = $vote->commentvote_down;
}
return array($upvotes, $downvotes);
}
function banned_by($users) {
global $db, $current_user;
$i_am_banned_by = array();
if (empty($users)) return $i_am_banned_by;
$userl = implode(',', $users);
$banners = $db->get_results("select rel_self_id from relationships where rel_self_id in ($userl) and rel_other_id=$current_user->user_id and rel_i_ban > 0");
foreach ($banners as $banner) $i_am_banned_by[ $banner->rel_self_id ] = 1;
return $i_am_banned_by;
}
function post_id2path($post_id) {
global $db;
if (!$post_id) return;
if ($post = get_post($post_id)) {
$yyyymmdd = substr(($post->post_date), 0, 10);
$slug = strtolower(preg_replace('|\W+|', '-', $post->post_title)); // url-safe pretty chars only; not used for navigation, only for seo and humans
$slug = trim($slug, '-'); // remove leading and trailing dashes
$path = "/$post->post_id/$yyyymmdd-$slug";
}
return $path;
}
function share_mail($share_ID) { // Pull share info from db and mail it out.
global $db, $user_id, $current_user;
if ($share_row = $db->get_row("select * from shares where share_ID=$share_ID")) {
$sender_email = $current_user->user_email;
// Actually send the email, but make reply-to be the user's email address.
$uurl = urlencode($share_row->share_url);
$utitle = urlencode($share_row->share_title);
$title = stripslashes($share_row->share_title);
$url = stripslashes($share_row->share_url);
$receiver_ID = intval($db->get_var("select user_id from users where user_email='$share_row->share_mailto'"));
if (!$receiver_ID) { // Create a new user in users if dne yet.
$user_name = substr(md5($share_row->share_mailto), 0, 4);
$plain_pass = substr(md5(microtime() ), 0, 8);
$sql = "insert into users (user_pass, user_email, user_registered, user_name)
values (md5('$plain_pass'), '$share_row->share_mailto', now(), '$user_name')";
$db->query($sql);
$receiver_ID = $db->get_var("select last_insert_ID()"); // Get the ID of the newly created user.
// However we got receiver_ID, update it with a key they can use to log in via linkcomment.php
$key = substr(md5(microtime()), 0, 8);
$db->query("update users set user_activation_key='$key' where user_id=$receiver_ID");
}
if (strlen($share_row->share_comment)) $message = stripslashes($share_row->share_comment) . "<p>\n\n";
else $message = "$sender_email is sharing this link with you:<p>\n\n";
$message .= "<a href='$url'>$url</a><hr>\n\n";
$message .= "Shared with you by $sender_email using the <a href='https://patrick.net/Page+Sharer'>Patrick.net Page Sharer</a><p>\n";
format_mail($share_row->share_mailto, $title, $message);
format_mail('p@patrick.net', 'new share: ' . $title, $message); // so I can see how much it's being used
$db->query("update shares set share_sent=now(), share_sender=$current_user->user_id where share_ID=$share_ID");
}
else die("No such share: $share_ID");
}
function update_postview($user_id, $post_id) {
global $db;
$sql = "insert into postviews (postview_user_id, postview_post_id, postview_last_view) values ($user_id, $post_id, now()) on duplicate key update postview_last_view=now()";
$db->query($sql);
}
function update_want_email($user_id, $post_id, $want_email) {
global $db;
$sql = "insert into postviews ( postview_user_id, postview_post_id, postview_want_email) values
($user_id, $post_id, $want_email) on duplicate key update postview_want_email=$want_email";
$db->query($sql);
}
// Add ref=patrick.net to a url.
function brandit($url) {
if (!$url) return;
if (!preg_match('/patrick.net/i', $url)) { // Brand it iff url does NOT already have patrick.net in it somewhere.
if (preg_match('/(.*)\?(.*)/', $url, $matches)) { // If E parms, add in ref=patrick.net as first one to make it visible and hard to remove
$loc = $matches[1];
$querystring = $matches[2];
$url = "$loc?ref=patrick.net&$querystring";
}
elseif (preg_match('/(.*)#(.*)/', $url, $matches)) { // If E hash tag, add in brand BEFORE that.
$loc = $matches[1];
$hashstring = $matches[2];
$url = "$loc?ref=patrick.net#$hashstring";
}
else { // Otherwise, we're the only parm.
$url .= '?ref=patrick.net';
}
}
return $url;
}
function follow($follow_ID, $comment_id=0) {
global $db, $user_id, $current_user;
if ($user_id == $follow_ID) return;
if ($comment_id > 0) $commentparm = "&comment=$comment_id"; // So that we pop right back to the same comment if we were on one.
$f = get_userrow($follow_ID);
$followbutton = '<button type="button" class="btn btn-default btn-xs">follow</button>';
if ($user_id) {
if (intval($current_user->i_follow[$follow_ID])) {
return "following (<a href='/users.php?followers=$f->user_id'>$f->user_followers</a>)" .
" <a href='/unfollow.php?unfollow=$follow_ID$commentparm' title='Stop getting new posts by $f->user_name' >x</a>";
}
else {
$s = "<a href='/follow.php?follow=$follow_ID$commentparm' title='Get new posts from $f->user_name by email' >$followbutton</a>";
if ($f->user_followers > 0) $s .= " (<a href='/users.php?followers=$f->user_id'>$f->user_followers</a>)";
return $s;
}
}
else return "<a href='/login.php?action=registerform' title='Register to get emails of new posts by $f->user_name' >$followbutton</a>";
}
function longest_strings_in_array($array) {
$mapping = array_combine($array, array_map('strlen', $array));
return array_keys($mapping, max($mapping));
}
function minimize_title($post_title) {
// Choose the longest section of title since it's probably the relevant part of the title.
$longest = longest_strings_in_array(preg_split('/[:|]/', $post_title));
$post_title = $longest[0];
$post_title = trim($post_title);
$post_title = preg_replace('/^[:|-]+/', '', $post_title);
$post_title = preg_replace('/[:|-]+$/', '', $post_title);
return $post_title;
}
function stopstrings($post_title) { // Clean up the submitted post title.
$post_title = preg_replace('/[^(\x20-\x7F)]/', '', $post_title); // Delete non-ascii chars in titles.
$post_title = str_replace('&#039;', "'", $post_title);
$post_title = str_replace('&#39;', "'", $post_title);
$post_title = str_replace('&#038;', '&', $post_title);
$post_title = str_replace('&#38;', '&', $post_title);
$post_title = str_replace('&quot;', '"', $post_title);
return $post_title;
}
function asciify($input) { // Replace non-ascii strings with ascii equivs.
$input = preg_replace('//', "'", $input);
return $input;
}
// The function rel_time() accepts two parameters: $from and $to. For best
// results, provide them as UNIX timestamps (derived from PHP's time() function).
// They also accept formats supported by strtotime(). $to is an optional argument
// and defaults to the current time.
// The function will calculate the difference between $from and $to. If $from
// occurs after $to, the function will substitue "ago" with "from now."
function rel_time($from, $to = null) {
$to = (($to === null) ? (time()) : ($to));
$to = ((is_int($to)) ? ($to) : (strtotime($to)));
$from = ((is_int($from)) ? ($from) : (strtotime($from)));
$units = array
(
"year" => 29030400, // seconds in a year (12 months)
"month" => 2419200, // seconds in a month (4 weeks)
"week" => 604800, // seconds in a week (7 days)
"day" => 86400, // seconds in a day (24 hours)
"hour" => 3600, // seconds in an hour (60 minutes)
"minute" => 60, // seconds in a minute (60 seconds)
"second" => 1 // 1 second
);
$diff = abs($from - $to);
$suffix = (($from > $to) ? ("from now") : ("ago"));
if ($from == $to) return "this very second";
foreach ($units as $unit => $mult)
if ($diff >= $mult) {
//$and = (($mult != 1) ? ("") : ("and "));
$and = "";
$output .= ", ".$and.intval($diff / $mult)." ".$unit.((intval($diff / $mult) == 1) ? ("") : ("s"));
$diff -= intval($diff / $mult) * $mult;
break; // So that we don't get and n days and n hours and n minutes and n seconds...
}
$output .= " ".$suffix;
$output = substr($output, strlen(", "));
return $output;
}
function reset_latest_comment($post_id) { // Reset post table data about latest comment, esp post_modified time.
global $db;
if (!$post_id) return;
if ($c = $db->get_row("select * from comments where comment_post_id=$post_id and comment_approved > 0 order by comment_date desc limit 1")) {
list($post_latest_comment_excerpt, $more_wc) = first_words(strip_tags($c->comment_content), 40);
$post_latest_comment_excerpt = mysql_real_escape_string(stripslashes($post_latest_comment_excerpt));
$commenter = get_userrow($c->comment_author)->user_name;
$post_latest_comment_excerpt = "$commenter says " . $post_latest_comment_excerpt;
$db->query("update posts set post_latest_comment_excerpt='$post_latest_comment_excerpt' where post_id=$c->comment_post_id");
$post_comments = intval($db->get_var("select count(*) from comments where comment_post_id=$post_id and comment_approved=1"));
$db->query("update posts set
post_modified='$c->comment_date',
post_comments=$post_comments,
post_latest_comment_id=$c->comment_id,
post_latest_commenter_id=$c->comment_author,
post_latest_comment_excerpt='$post_latest_comment_excerpt'
where post_id=$post_id"); // post_modified is necessary for sorting posts by latest comment.
}
else { // There are no comments.
$db->query("update posts set
post_modified=post_date,
post_comments=0,
post_latest_comment_id=0,
post_latest_commenter_id=0,
post_latest_comment_excerpt=''
where post_id=$post_id");
}
return get_post($post_id);
}
function approval_actions($comment_id) { // Happens when a comment gets approved.
global $db, $user_id;
$comment = $db->get_row("select * from comments where comment_id=$comment_id");
extract((array) $comment);
list($post_latest_comment_excerpt, $more_wc) = first_words( strip_tags($comment_content), 40 );
$post_latest_comment_excerpt = get_userrow($comment_author)->user_name . " says " . mysql_real_escape_string(stripslashes($post_latest_comment_excerpt));
$post_comments = intval($db->get_var("select count(*) from comments where comment_post_id=$comment_post_id"));
$db->query("update posts set
post_modified=now(),
post_comments=$post_comments,
post_latest_comment_id=$comment_id,
post_latest_commenter_id=$comment_author,
post_latest_comment_excerpt='$post_latest_comment_excerpt'
where post_id=$comment_post_id");
// preg_match all of those /proc/<pid>/environ to find REQUEST_URI=/$comment_post_id
// send php ipc message to local ws.php so websocket will forward it to all browsers who are on page comment_post_id
foreach (glob("/proc/*/environ") as $file) {
if ($contents = @file_get_contents($file)) {
$pattern = "|REQUEST_URI=/$comment_post_id|";
if (preg_match($pattern, $contents)) {
$pid = explode('/', $file)[2];
$queue = msg_get_queue($pid);
$object = new stdclass;
$object->name = 'foo';
$object->id = uniqid();
$object->body = format_comment($comment, $post_comments, $comment_post_id, 0, 0);
// remove newlines and carriage returns because messages are delimited by newslines
$object->body = str_replace(array("\n", "\r"), '', $object->body);
if (msg_send($queue, 1, $object)) {
//echo "added to queue \n";
//print_r(msg_stat_queue($queue));
}
else {
//echo "could not add message to queue \n";
}
}
}
}
commentmail($comment_id);
}
function commentmail($comment_id) { // Reasons to send out comment emails: @user summons, user watching post
global $db;
extract((array) $db->get_row("select * from comments where comment_id=$comment_id"));
if (!$comment_post_id) return;
$post_title = strip_tags($db->get_var("select post_title from posts where post_id=$comment_post_id"));
$commenter = real_if_private($comment_post_id, $comment_author);
// If comment_content contains a summons like "@<user>", and user is user_summonable, then email user.
if ( preg_match('/@([\w.,-]+)/i', $comment_content, $matches) || preg_match('/@"([\w .,-]+)"/i', $comment_content, $matches) ) {
$summoned_user_username = $matches[1];
if ($u = $db->get_row("select * from users where user_name='$summoned_user_username' and user_id != '$comment_author' and user_summonable=1")) {
$subject = "New Patrick.net comment by $commenter directed at $summoned_user_username";
$notify_message = '<html><body><head><base href="https://patrick.net/" ></head>';
$notify_message .= "New comment by $commenter in <A HREF='https://patrick.net" . post_id2path($comment_post_id) .
"'>$post_title</A>:<p>\r\n\r\n";
$notify_message .= "<p>$comment_content<p>\r\n\r\n";
$notify_message .= "<p><A HREF='https://patrick.net". post_id2path($comment_post_id) .
"?c=$comment_id#comment-$comment_id'>Reply</A><p>\r\n\r\n";
$notify_message .= "<font size='-1'>Stop allowing <A HREF='https://patrick.net/profile.php" .
"#user_summonable'>summons</A></font><br>\r\n\r\n";
$notify_message .= '</body></html>';
format_mail($u->user_email, $subject, $notify_message);
format_mail('p@patrick.net', $subject, $notify_message); // Just so I have a record.
$already_mailed[$u->user_id]++; // Include in already_mailed hash so we don't duplicate emails below.
}
}
// $comment_author is the commenter logged in right now, who probably doesn't want to get his own comment in email.
// Select all other subscriber user_id's and send them the comment by mail.
$sql = "select postview_user_id, postview_post_id from postviews
where postview_post_id=$comment_post_id and postview_want_email=1 and postview_user_id != $comment_author
group by postview_user_id"; // Group by so that user_id is in there only once.
if ($rows = $db->get_results($sql)) {
foreach ($rows as $row) {
if ($already_mailed[$row->postview_user_id]) continue;
$u = get_userrow($row->postview_user_id);
$subject = "New Patrick.net comment in '$post_title'";
$notify_message = '<html><body><head><base href="https://patrick.net/" ></head>';
$notify_message .= "New comment by $commenter in <A HREF='https://patrick.net" . post_id2path($comment_post_id) .
"'>$post_title</A>:<p>\r\n\r\n";
$notify_message .= "<p>$comment_content<p>\r\n\r\n";
$notify_message .= "<p><A HREF='https://patrick.net". post_id2path($comment_post_id) .
"?c=$comment_id#comment-$comment_id'>Reply</A><p>\r\n\r\n";
$notify_message .= "<font size='-1'>Stop watching <A HREF='https://patrick.net". post_id2path($comment_post_id) .
"?want_email=0'>$post_title</A></font><br>\r\n\r\n";
$notify_message .= "<font size='-1'>Stop watching <A HREF='https://patrick.net/autowatch.php" .
"?off=true'>all posts</A></font><br>\r\n";
$notify_message .= '</body></html>';
format_mail($u->user_email, $subject, $notify_message);
$already_mailed[$row->postview_user_id]++; // Include in already_mailed hash so we don't duplicate emails below.
}
}
}
function block_nuked($ip_address) {
global $db;
if (!strlen($ip_address)) return; // So that we don't search for the empty IP ''
if ($db->get_var("select nuke_date from nukes where nuke_ip = '$ip_address' limit 1")) {
// Prevent nukes log from growing without bound. This means nuking is only temporary.
//if (intval($db->get_var("select count(*) from nukes")) > 10000) $db->query("delete from nukes order by nuke_date limit 1000");
redirect('https://www.amazon.com/Housing-Trap-Buyers-Captured-Yourself/dp/1479156213/?tag=patricknet-20');
}
}
function key2user($key) {
global $db;
$key = preg_replace('/[^a-z0-9]/i', '', $key);
if (!$key) return 0; // so that we never search for the empty key
if ($user = $db->get_row("select * from users where user_activation_key = '$key'"))
return $user;
else
return 0;
}
function login($user_id, $md5_pass) {
global $db, $current_user;
if ( check_pw($user_id, $md5_pass) ) {
set_user($user_id);
p_setcookie($user_id, $md5_pass);
$ip = $_SERVER['REMOTE_ADDR'];
$sql = "insert into ips (ip_user_id, ip_addr) values($user_id, '$ip') on duplicate key update ip_ts=now()";
$db->query($sql);
return true;
}
else return false;
}
function is_robot() {
$botstrings = array('Googlebot', 'bingbot', 'msnbot', 'discobot', 'YandexBot', 'slurp', 'Baiduspider', 'spider', 'crawler', 'bot$');
foreach ($botstrings as $botstring) {
if (preg_match("/$botstring/i", $_SERVER['HTTP_USER_AGENT'])) return true;
}
return false;
}
function first_words($string, $num) {
$words = preg_split('/\s+/', $string);
$firstwords = array_slice($words, 0, $num);
$shorter = array();
foreach ($firstwords as $word) {
if (preg_match('/http/i', $word)) continue;
$shorter[] = substr($word, 0, 30);
}
if (sizeof($words) > sizeof($shorter)) // then we want to add ...
return array (implode(' ', $shorter), sizeof($words) - sizeof($shorter)); // Say how many more words E.
else
return array (implode(' ', $shorter), 0);
}
/*
add_query_arg: Returns a modified querystring by adding
a single key & value or an associative array.
Setting a key value to emptystring removes the key.
Omitting oldquery_or_uri uses the $_SERVER value.
Parameters:
add_query_arg(newkey, newvalue, oldquery_or_uri) or
add_query_arg(associative_array, oldquery_or_uri)
*/
function add_query_arg() {
$ret = '';
if ( is_array(func_get_arg(0)) ) {
if ( @func_num_args() < 2 || false === @func_get_arg(1) )
$uri = $_SERVER['REQUEST_URI'];
else
$uri = @func_get_arg(1);
} else {
if ( @func_num_args() < 3 || false === @func_get_arg(2) )
$uri = $_SERVER['REQUEST_URI'];
else
$uri = @func_get_arg(2);
}
if ( $frag = strstr($uri, '#') )
$uri = substr($uri, 0, -strlen($frag));
else
$frag = '';
if ( preg_match('|^https?://|i', $uri, $matches) ) {
$protocol = $matches[0];
$uri = substr($uri, strlen($protocol));
} else {
$protocol = '';
}
if (strpos($uri, '?') !== false) {
$parts = explode('?', $uri, 2);
if ( 1 == count($parts) ) {
$base = '?';
$query = $parts[0];
} else {
$base = $parts[0] . '?';
$query = $parts[1];
}
} elseif (!empty($protocol) || strpos($uri, '/') !== false) {
$base = $uri . '?';
$query = '';
} else {
$base = '';
$query = $uri;
}
p_parse_str($query, $qs);
$qs = urlencode_deep($qs);
if ( is_array(func_get_arg(0)) ) {
$kayvees = func_get_arg(0);
$qs = array_merge($qs, $kayvees);
} else {
$qs[func_get_arg(0)] = func_get_arg(1);
}
foreach($qs as $k => $v) {
if ( $v !== FALSE ) {
if ( $ret != '' )
$ret .= '&';
if ( empty($v) && !preg_match('|[?&]' . preg_quote($k, '|') . '=|', $query) )
$ret .= $k;
else
$ret .= "$k=$v";
}
}
$ret = trim($ret, '?');
$ret = $protocol . $base . $ret . $frag;
$ret = rtrim($ret, '?');
return $ret;
}
function add_magic_quotes($array) {
global $db;
//var_dump(debug_backtrace()); die;
foreach ($array as $k => $v) {
if ( is_array($v) ) {
$array[$k] = add_magic_quotes($v);
} else {
$array[$k] = $db->escape($v);
}
}
return $array;
}
function nocache_headers() {
@ header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
@ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
@ header('Cache-Control: no-cache, must-revalidate, max-age=0');
@ header('Pragma: no-cache');
}
function single_post_js($post_id) {
global $current_user;
?>
<script type="text/javascript"><!--
function addquote(post_id, comment_id, author) {
var comment_link;
var textarea = document.forms['commentform'].elements['comment']
var theSelection = ''
if (comment_id > 0)
comment_link = '<a href="/?p=' + post_id + '&c=' + comment_id + '#comment-' + comment_id + '">' + author + ' says</a>'
else
comment_link = '<a href="/?p=' + post_id + '">' + author + ' says</a>'
if (theSelection = getHTMLOfSelection()) { // user manually selected something
if (s = sessionStorage.getItem('tripleclickselect')) { // override tripleclick selection to avoid getting extra html elements
theSelection = s.trim() // trim bc tripleclick appends useless whitespace
sessionStorage.removeItem('tripleclickselect') // so we don't keep using it by mistake
}
}
else { // either we are on mobile (no selection possible) or the user did not select any text
theSelection = document.getElementById('comment-' + comment_id + '-text').innerHTML // whole comment, or post when comment_id == 0
}
if (theSelection.length > 1024) var theSelection = theSelection.substring(0, 1000) + '...' // might mangle tags
textarea.value = textarea.value + comment_link + '<br>\n<blockquote>' + theSelection + '\n</blockquote>\n\n'
textarea.focus()
return
}
window.addEventListener('click', function (evt) {
if (evt.detail === 3) {
sessionStorage.setItem('tripleclickselect', window.getSelection());
// if they don't use it by clicking "quote" within 10 seconds, delete it so it dn confuse them later
setTimeout(function(){
sessionStorage.removeItem('tripleclickselect');
}, 10000);
}
});
function getHTMLOfSelection () {
var range
if (window.getSelection) {
var selection = window.getSelection()
if (selection.rangeCount > 0) {
range = selection.getRangeAt(0)
var clonedSelection = range.cloneContents()
var div = document.createElement('div')
div.appendChild(clonedSelection)
return div.innerHTML
}
else {
return ''
}
}
else {
return ''
}
}
function on_mobile() {
if (navigator.userAgent.match(/Android/i)
|| navigator.userAgent.match(/webOS/i)
|| navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)
|| navigator.userAgent.match(/BlackBerry/i)
|| navigator.userAgent.match(/Windows Phone/i)
) {
return true
}
else {
return false
}
}
-->
// We include $post_id in the ws url so it can be used to identify that post's open websocket pids on the server.
// Then in approval_actions($comment_id) we can then instantly send the comment to those browsers via those pid's stdout
var ws = new WebSocket('wss://<?= DOMAIN ?>:7070/<?= $post_id ?>');
ws.onmessage = function(event) { // append latest comment to dom
document.getElementById('liveComments').insertAdjacentHTML( 'beforebegin', event.data );
}
function textCounter(field, counter, maxlimit) {
var countfield = document.getElementById(counter);
if (field.value.length > maxlimit) {
field.value = field.value.substring(0, maxlimit);
return false;
} else {
countfield.value = maxlimit - field.value.length;
}
}
</script>
<?
}
function send_post_confirmation_mail($post_id) {
global $db;
$u = get_userrow(get_post($post_id)->post_author);
$key = substr(md5(microtime()), 0, 8);
$db->query("update users set user_activation_key='$key' where user_id=$u->user_id");
$uredirect_to = urlencode(post_id2path($post_id));
$umessage = "Click to confirm your post on Patrick.net:<p>\r\n\r\n";
$umessage .= "<a href='https://patrick.net/login.php?action=confirm_post&key=$key&redirect_to=$uredirect_to'>";
$umessage .= "https://patrick.net/login.php?action=confirm_post&key=$key&redirect_to=$uredirect_to</a>\r\n\r\n";
format_mail($u->user_email, 'Confirm patrick.net post', $umessage);
}
function get_whitelist($post_id) { // return whitelist of users for a private post
global $db, $current_user;
$sql = "select user_name, user_id from users where user_id in (select whitelist_user_id from whitelists where whitelist_post_id=$post_id) order by user_name";
if ($rows = $db->get_results($sql)) {
foreach ($rows as $row) $whitelist[$row->user_id] = $row->user_name;
}
return $whitelist;
}
function do_single($post_id) {
global $db, $current_user;
$post = get_post($post_id);
$user_id = $current_user->user_id;
if ($post->post_private) {
$whitelist = get_whitelist($post_id);
if (!$whitelist[$current_user->user_id]) format_die("You are not on the whitelist for post $post_id");
}
if (isset($_GET['want_email']) && !is_user_logged_in() ) redirect('/login.php?action=registerform');
get_header($post->post_title);
if (!$post->post_approved) {
send_post_confirmation_mail($post_id);
print "<h3><font color='red'>Check your email for confirmation link. Reload this page to resend email.</font></h3>";
}
if ($post->post_private) {
foreach($whitelist as $user_id => $user_name) {
$u = get_userrow($user_id);
if (strlen($u->user_realname)) $users[] = "<a href='/users/$user_name'>$u->user_realname</a>";
else $users[] = "<a href='/users/$user_name'>$user_name</a>";
}
print "<p><p>The post below is a <a href='/1303826/2017-03-12-private-group-chat-button'>private group chat</a> with users " .
implode(', ', $users) . '<br>';
?>
<form method="get" action="/invite.php" >
<div class='form-group'>
<input id="invite" name="invite" type="text" placeholder="INVITE more people via emails or usernames and hit enter" class="form-control" >
</div>
<input type='hidden' name='post_id' value='<?= $post_id ?>' />
</form>
<script>
document.getElementById("invite").focus();
</script>
<?
}
single_post_js($post_id);
post_contents($post_id);
post_comment_list($post_id);
// if this post has a non-patrick.net referer, log that and give the user credit if he doesn't already have it
if (isset($_SERVER['HTTP_REFERER'])) {
if (!preg_match("|https?://(www.)?patrick.net|i", $_SERVER['HTTP_REFERER'])) { // count only referers not from patrick.net itself
$referer_url = mysql_real_escape_string($_SERVER['HTTP_REFERER']);
$sql = "insert into referers (referer_author_id, referer_post_id, referer_url) values ($post->post_author, $post_id, '$referer_url')
on duplicate key update referer_usage_count=referer_usage_count + 1";
$db->query($sql);
$sql = "update posts set post_referers=(select count(*) from referers where referer_post_id=$post_id) where post_id=$post_id";
$db->query($sql);
$sql = "update users set user_referers=(select count(*) from referers where referer_author_id=$post->post_author) where user_id=$post->post_author";
$db->query($sql);
}
}
get_footer();
}
function post_contents($post_id) {
global $db, $user_id, $post, $current_user;
get_user(); // To set $user_id.
$post = get_post($post_id);
if (!$post) die("No such post"); // Necessary to prevent redirect below from looping.
$docroot = $_SERVER["DOCUMENT_ROOT"];
setup_postdata($post);
$post_id = $post->post_id;
$author = $post->post_author;
$u = get_userrow($author);
$post_views = $post->post_views + 1;
$db->query("update posts set post_views=post_views+1 where post_id=$post_id");
?><div class="comment" ><?
if ( is_user_logged_in() ) { // Iff user is logged in, then allow post subscription updates.
if ( isset($_GET['want_email']) ) {
$want_email = intval($_GET['want_email']);
if ($want_email)
$change = '<font color="green">watch added</font>';
else
$change = '<font color="red">watch removed</font>';
update_want_email($user_id, $post_id, $want_email);
}
update_postview($user_id, $post_id);
}
print get_like_dislike($post);
print icon($u->user_id,1,1,1) . ' '; // $icon_owner, $scale=1, $alt=1, $left=0
$slug = preg_replace('|\W|', '-', $post->post_title); // url-safe and pretty chars only; not used for navigation, only for seo and humans
if ($post->post_private) $red = 'style="color: red;"';
$path = post_id2path($post_id);
print "<h2 style='display:inline' $red ><a href='$path'>$post->post_title</a></h2>";
print "<p>By " . name_posts($u->user_id);
print ' ';
print follow($u->user_id);
print ' &nbsp; ';
print zdate($post->post_date);
print ' &nbsp; ';
if ($current_user->user_pbias >= 3 or $post->post_private) {
$ts = time();
$nonce=get_nonce($ts);
$nonce_parms = "ts=$ts&nonce=$nonce";
$confirm_adhom = 'onClick="javascript:return confirm(\'Really mark as ad hominem?\')"';
print " <a href='/adhominem.php?post_id=$post->post_id&$nonce_parms' $confirm_adhom title='attacks person, not point' >ad hominem</a> &nbsp; ";
}
if ($post->post_referers) {
$s = $post->post_referers == 1 ? "" : "s";
$n = number_format($post->post_referers);
$uusername = urlencode($u->user_name);
print "<a href='/links.php?user_name=$uusername&post_id=$post->post_id' title='Incoming links to this post' >$n link$s<img src='/images/goldstar.gif'
width='18' height='17'></a> &nbsp; ";
}
$s = $post->post_comments == 1 ? "" : "s";
print number_format($post_views) . " views &nbsp; $post->post_comments comment$s &nbsp; ";
$watchers = intval($db->get_var("select count(*) from postviews where postview_post_id=$post_id and postview_want_email=1"));
$want_email = intval(get_postview_row($post_id, $user_id)->postview_want_email);
if ($want_email)
echo "<A HREF='". post_id2path($post_id) ."?want_email=0' title='stop getting comments by email' >
<IMG SRC='/content/openeye.png'> unwatch</A> ($watchers) $change &nbsp;";
else
echo "<A HREF='". post_id2path($post_id) ."?want_email=1' title='Get comments by email for this post' >
<IMG SRC='/content/closedeye.png'> watch</A> ($watchers) $change &nbsp;";
if ( $post->post_content != '' && $post_id) { // If there is some post content, let them quote it.
?>
<a href="#commentform" onclick="addquote( '<?= $post->post_id ?>', '0', '<? echo $u->user_name; ?>' ); return false;"
title="Select some text then click this to quote" >quote</a> &nbsp;
<?
}
print ' &nbsp; ' . share_post($post) . ' &nbsp; ';
if ( ($user_id == $post->post_author) || ($current_user->user_level >= 4) ) {
$location = "/edit.php?p=$post->post_id";
print "<a href=\"$location\">edit</a> &nbsp; ";
}
if (($user_id == $post->post_author && !$post->post_comments) || ($current_user->user_level >= 4)) {
$ts = time();
$nonce=get_nonce($ts);
$nonce_parms = "ts=$ts&nonce=$nonce";
?><A HREF="/delete_post.php?post_id=<?= $post->post_id ?>&<?= $nonce_parms ?>" onClick='javascript:return confirm("Really?")' >delete</A> &nbsp; <?
}
?><p><div class="entry" class="alt" id="comment-0-text" ><?
print $post->post_content;
?></div><?
?></div><?
}
function mkdir_p($target) {
// from php.net/mkdir user contributed notes
if (file_exists($target)) {
if (! @ is_dir($target))
return false;
else
return true;
}
// Attempting to create the directory may clutter up our display.
if (@ mkdir($target)) {
$stat = @ stat(dirname($target));
$dir_perms = $stat['mode'] & 0007777; // Get the permission bits.
@ chmod($target, $dir_perms);
return true;
} else {
if ( is_dir(dirname($target)) )
return false;
}
// If the above failed, attempt to create the parent node, then try again.
if (mkdir_p(dirname($target)))
return mkdir_p($target);
return false;
}
function upload_dir() {
$dir = ABSPATH . 'content/uploads';
// Generate the yearly and monthly dirs if dne
$time = date('Y-m-d H:i:s');
$y = substr( $time, 0, 4 );
$m = substr( $time, 5, 2 );
$dir = $dir . "/$y/$m";
if (!mkdir_p($dir)) format_die("Unable to create directory $dir. Is its parent directory writable by the server?");
return $dir;
}
function format_die( $message, $title = '' ) {
get_header();
?><p>&nbsp;<p><h2><?= $message; ?></h2><p>&nbsp;<p><?
get_footer(); exit;
}
function parse_args( $args, $defaults = '' ) {
if ( is_array( $args ) )
$r =& $args;
else
p_parse_str( $args, $r );
if ( is_array( $defaults ) )
return array_merge( $defaults, $r );
else
return $r;
}
// Setup global post data.
function setup_postdata($post) {
global $id, $postdata, $authordata, $day, $page, $pages, $more;
$id = (int) $post->post_id;
if (isset($_GET['s']))
$authordata = get_userrow(0); // Do get userdata for search results, because it can be hundreds.
else
$authordata = get_userrow($post->post_author);
$day = date('d.m.y', strtotime($post->post_date));
if ( !$page ) $page = 1;
$content = $post->post_content;
return true;
}
function icon($icon_owner, $scale=1, $alt=1, $left=0) { // wrapper for icon_from_userrow(); starts with user_id, causes db call in get_userrow()
$u = get_userrow($icon_owner);
return icon_from_userrow($u, $scale, $alt, $left);
}
function icon_from_userrow($u, $scale=1, $alt=1, $left=0) { // so we don't have to make db call, better for perf in lists of user objects
global $db;
if (preg_match('/\.(jpg|jpeg|gif|png|bmp)$/i', $u->user_icon)) {
if ($u->user_icon_width > 200 || !$u->user_icon_width || !$u->user_icon_height) { // This stuff will happen only if icon doesn't have width and height, or too big.
$img_parms = @getimagesize($_SERVER['DOCUMENT_ROOT'] . $u->user_icon); // @ char will suppress warnings
$u->user_icon_width = intval($img_parms[0]);
$u->user_icon_height = intval($img_parms[1]);
$db->query("update users set user_icon_width=$u->user_icon_width, user_icon_height=$u->user_icon_height where user_id = $icon_owner");
if (function_exists('apc_delete')) apc_delete("users:$icon_owner");
}
$user_icon_width = intval($u->user_icon_width * $scale);
$user_icon_height = intval($u->user_icon_height * $scale);
if ($left) $align="align='left' hspace='5' vspace='2' ";
return "<a href='/users/" . $u->user_name . "' ><img src='$u->user_icon' width='$user_icon_width' height='$user_icon_height' $align ></a>";
}
else return "";
}
function user_realname($user_id) {
if (!$user_id) return "anonymous";
$u = get_userrow($user_id);
if (strlen($u->user_realname))
return "<a href='/users/$u->user_name' >$u->user_realname</a>";
else
return name_posts($user_id);
}
function name_posts($user_id) {
if (!$user_id) return "anonymous";
$u = get_userrow($user_id);
return "<a href='/users/$u->user_name' >$u->user_name</a>";
}
function show_civility($user_id) {
if (!$user_id) return '';
$u = get_userrow($user_id);
if (!$name_only and $u->user_civil_comments > 0) {
$total = $u->user_civil_comments + $u->user_adhom_comments;
$ratio = intval(100 * $u->user_civil_comments / $total);
return "$u->user_civil_comments/$total = $ratio% <a href='/1303051/2017-02-16-user-civility-score'>civil</a>";
}
}
function get_author_name( $auth_id ) {
$authordata = get_userrow( $auth_id );
return $authordata->user_name;
}
function set_user($id) { // Sets the currently logged-in user object.
global $db, $current_user, $user_id;
if (isset($current_user) && ($id == $current_user->user_id)) return $current_user;
$current_user = get_userrow($id);
$user_id = $current_user->user_id;
// Get hash of everyone I like and everyone I'm banning. rel_i_ban is spelled that way because ignore is a reserved word in mysql.
$relationships = $db->get_results("select * from relationships where rel_self_id = $id");
if ($relationships) {
foreach ( $relationships as $relation ) {
$current_user->i_follow[$relation->rel_other_id] = $relation->rel_i_follow; // Will be set to unix timestamp if following current_user.
$current_user->i_like[$relation->rel_other_id] = $relation->rel_my_friend; // Will be set to unix timestamp if friend of current_user.
$current_user->i_have_banned[$relation->rel_other_id] = $relation->rel_i_ban; // Will be set to unix timestamp if banning current_user.
}
}
// Now go the other way, and find out who likes me. Now rel_self_id refers to the person who likes you, and rel_other_id is you.
$relationships = $db->get_results("select * from relationships where rel_other_id = $id");
if ($relationships) {
foreach ( $relationships as $relation ) {
$current_user->follows_me[$relation->rel_self_id] = $relation->rel_i_follow; // Will be set to unix timestamp if on current_user's follow list.
$current_user->is_banned_by[$relation->rel_self_id] = $relation->rel_i_ban; // Will be set to unix timestamp if on current_user's ban list.
$current_user->likes_me[$relation->rel_self_id] = $relation->rel_my_friend; // Will be set to unix timestamp if friend of current_user.
if ($current_user->likes_me[$relation->rel_self_id] && !$current_user->i_like[$relation->rel_self_id])
$current_user->unrequited = 1; // There are unrequited friendship requests to deal with.
}
}
return $current_user;
}
function get_user() { // Gets currently logged-in user object.
global $current_user, $db;
if (isset($current_user)) return $current_user;
// Otherwise, get the user_id from the cookie, set the global $current_user, and return it.
// Note that $_COOKIE['patricknetpass'] is already double-md5'd.
if ( !check_pw($_COOKIE['patricknetuser'], $_COOKIE['patricknetpass']) ) {
unset($GLOBALS['current_user']); // Necessary bc otherwise just unsets local copy.
return 0;
}
$user_id = $_COOKIE['patricknetuser'];
$current_user = set_user($user_id);
// update users online
$sql = "delete from onlines where online_last_view < date_sub(now(), interval 5 minute)";
$db->query($sql);
$sql = "insert into onlines (online_user_id, online_username, online_last_view) values ($current_user->user_id, '$current_user->user_name', now())
on duplicate key update online_last_view=now()";
$db->query($sql);
return $current_user;
}
function email2uid($email) {
global $db;
$email = sanitize_email($email);
$uid = intval($db->get_var("select user_id from users where user_email = '$email'"));
return $uid;
}
function username2uid($user_name) {
global $db;
$user_name = preg_replace('/[^a-zA-Z0-9._ -]/', '', substr($user_name, 0, 40));
$uid = intval($db->get_var("select user_id from users where user_name = '$user_name'"));
return $uid;
}
function get_userrow($user_id) {
global $db;
$user_id = (int) $user_id;
if ( $user_id == 0 ) return false;
if (function_exists('apc_fetch') && ($row = apc_fetch("users:$user_id"))) {
parse_str($row, $assoc);
return (object)$assoc;
}
else { // Get it from database and store in the APC cache.
if ($user_row = $db->get_row("select * from users where user_id = $user_id and 23=23")) {
if (function_exists('apc_store')) apc_store("users:$user_id", http_build_query($user_row), 600);
return $user_row;
}
else return false;
}
}
function format_mail($to, $subject, $message) {
$headers = "MIME-Version: 1.0\nFrom: p@patrick.net\nContent-Type: text/html; charset=\"UTF-8\"\n";
if ('localdev' == gethostname()) { // for testing without mail
$message = preg_replace('/patrick.net/', 'localhost:4433', $message);
$date = date('Y-m-d H:i:s');
$h = fopen('/tmp/mails.html','ab');
fwrite($h, "\n\n----\n<p>$date<p>headers=$headers<p>to=$to<p>subject=$subject<p>message=$message");
fclose($h);
}
elseif (is_email($to)) mail($to, $subject, $message, $headers);
}
function check_pw($user_id, $md5_pass) {
global $db, $error;
$user_id = intval($user_id);
if ( '' == $user_id ) return false;
if ( '' == $md5_pass ) return false;
//$login = $db->get_row("select * from users where user_id = $user_id");
$login = get_userrow($user_id);
if (!$login) {
p_clearcookie();
die("No such user_id in users table: $user_id");
}
if ( $login->user_pass == $md5_pass ) // Note that users.user_pass is stored md5'd.
return true;
else
return false;
}
function is_user_logged_in() {
global $current_user;
if ($current_user = get_user()) return 1;
else return 0;
}
function auth_redirect() {
// Checks if a user is logged in, if not redirects them to the login page
if ( (!empty($_COOKIE['patricknetuser']) && !check_pw($_COOKIE['patricknetuser'], $_COOKIE['patricknetpass'], true)) || (empty($_COOKIE['patricknetuser'])) ) {
nocache_headers();
//$uc = $_COOKIE['patricknetuser'];
//$pc = $_COOKIE['patricknetpass'];
//format_die("$uc -> $pc");
redirect('/index.php');
}
}
function redirect($location) {
if ( !$location ) return false;
// Prevent infinite redirection loops.
global $redir_count;
if ($redir_count++ > 3) {
$errormsg = "Too many redirects ($redir_count) to $location";
format_mail('p@patrick.net', 'A Phadraig: infinite redirect loop', $errormsg);
format_die($errormsg);
}
$location = preg_replace('| |i', '%20', $location);
$location = preg_replace('|[^a-z0-9-~+_.?#=&;,/:%@]|i', '', $location);
$location = kses_no_null($location);
$strip = array('%0d', '%0a');
$location = str_replace($strip, '', $location);
header("Location: $location");
exit;
}
function p_setcookie($user_id, $md5_pass) {
$expire = time() + 31536000;
setcookie('patricknetuser', $user_id, $expire, '/');
setcookie('patricknetpass', $md5_pass, $expire, '/');
}
function p_clearcookie() {
setcookie('patricknetuser', ' ', time() - 31536000, '/');
setcookie('patricknetpass', ' ', time() - 31536000, '/');
}
function get_user_to_edit( $the_user_id ) {
// First clear any cached data.
if (function_exists('apc_delete')) apc_delete("users:$the_user_id");
$user = set_user($the_user_id);
$user->user_email = attribute_escape($user->user_email);
$user->user_name = attribute_escape($user->user_name);
$user->user_icon = clean_url($user->user_icon);
return $user;
}
function get_comment_list($s, $start, $num) {
global $db;
$start = (int) $start;
$num = (int) $num;
if ( $s ) {
$s = substr($s, 0, 80); // Max search string is 80 chars.
$s = $db->escape($s);
$comments = $db->get_results("select SQL_CALC_FOUND_ROWS * from comments where MATCH(comment_content) AGAINST ('$s') order by comment_date desc LIMIT $start, $num");
} else format_die("Too many empty comments to count.");
$total = $db->get_var( "select FOUND_ROWS()" );
return array($comments, $total);
}
function get_comment_list_by_author( $a, $start, $num ) {
global $db;
$start = (int) $start;
$num = (int) $num;
if ( $a ) {
$comments = $db->get_results("select SQL_CALC_FOUND_ROWS * from comments where comment_author = $a ORDER BY comment_date DESC LIMIT $start, $num");
} else {
format_die("Need an author to get that author's comments");
}
$total = $db->get_var( "select FOUND_ROWS()" );
return array($comments, $total);
}
function get_comment_list_by_number( $n, $start, $num ) {
global $db;
$start = (int) $start;
$num = (int) $num;
$n = preg_replace('/[^0-9]/', '', substr($_GET['n'], 0, 20) );
$sql = "select SQL_CALC_FOUND_ROWS * from comments, users force index (user_comments_index) where comments.comment_author = users.user_id and user_comments = $n
ORDER BY comment_date DESC LIMIT $start, $num";
//print $sql;
$comments = $db->get_results($sql);
$total = $db->get_var( "select FOUND_ROWS()" );
return array($comments, $total);
}
function handle_upload( &$file ) {
if (!@is_uploaded_file($file['tmp_name'])) die('Specified file failed upload test.');
if (!($file['size'] > 0)) die('File is empty. Please upload something more substantial.');
$filename = $file['name']; // like hotwoman.jpg
$oldname = $file['tmp_name']; // like /tmp/phpbHIyfi (where file was uploaded to)
return clean_and_move($filename, $oldname);
}
function clean_and_move($filename, $oldname) {
if (preg_match( '/\.(jpg|jpeg|gif|gifv|png|bmp)$/i' , $filename, $matches))
$ext = '.' . $matches[1]; // include the dot, like .png
else
die('Image type not supported. Try a different image.');
// always lowercase uploads so we don't get collisions on stupid case-insensitive Mac fs between This.jpg and this.jpg, for example
$filename = strtolower($filename);
$filename = preg_replace('/[^\w\.-]/', '', $filename); // allow only alphanum, dot, dash in filename
if (strlen($filename) > 128 ) $filename = md5($filename) . $ext; // filename was too long to be backed up, so hash it to shorten it
$ud = upload_dir();
$number = '';
while ( file_exists( $ud . "/$filename" ) ) {
$filename = str_replace( "$number$ext", ++$number . $ext, $filename );
if ($number > 10) die("Too many copies of $filename");
}
// Move the file to the uploads dir
$newname = "$ud/$filename";
if (false === rename($oldname, $newname)) die("File $oldname could not be moved to $newname");
if (preg_match( '/\.(jpg|jpeg)$/i' , $newname, $matches) && file_exists('/usr/bin/jpegoptim') ) {
$output = shell_exec("/usr/bin/jpegoptim $newname 2>&1"); // minimize size of new jpeg
}
if (preg_match( '/\.(png)$/i' , $newname, $matches) && file_exists('/usr/bin/optipng') ) {
$output = shell_exec("/usr/bin/optipng $newname 2>&1"); // minimize size of new png
}
$stat = stat(dirname($newname));
@chmod($newname, $stat['mode'] & 0000666);
return $newname;
}
function create_image($file, $max_dim = 600) { // $max_dim is maximum allowed dimension in either direction
if (file_exists($file)) {
$image_attr = getimagesize($file);
if (empty($image_attr)) die("Not an image: $file");
else {
$width = $image_attr[0];
$height = $image_attr[1];
// 1 = GIF, 2 = JPEG, 3 = PNG, 6 = BMP
if ( ($image_attr[2] != 1) && ($image_attr[2] != 2) && ($image_attr[2] != 3) && ($image_attr[2] != 6) ) {
print_r($image_attr);
die("Filetype not supported");
}
else {
if ($width > $max_dim) {
$command = "/usr/bin/mogrify -resize $max_dim $file"; // Larger dimension will be reduced to $max_dim.
$output = shell_exec($command." 2>&1");
$image_attr = getimagesize( $file );
$width = $image_attr[0];
$height = $image_attr[1];
}
}
}
} else die("File not found: $file");
return array ($file, $width, $height);
}
function clean_pre($text) {
$text = str_replace('<br />', '', $text);
$text = str_replace('<p>', "\n", $text);
$text = str_replace('</p>', '', $text);
return $text;
}
function autop($pee, $br = 1) {
$pee = $pee . "\n"; // just to make things a little easier, pad the end
$pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
// Space things out a little
$allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
$pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
$pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
$pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
$pee = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $pee); // make paragraphs, including one at the end
$pee = preg_replace('|<p>\s*?</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
$pee = preg_replace('!<p>([^<]+)\s*?(</(?:div|address|form)[^>]*>)!', "<p>$1</p>$2", $pee);
$pee = preg_replace( '|<p>|', "$1<p>", $pee );
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
$pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
$pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
if ($br) {
$pee = preg_replace('/<(script|style).*?<\/\\1>/se', 'str_replace("\n", "<PreserveNewline />", "\\0")', $pee);
$pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
$pee = str_replace('<PreserveNewline />', "\n", $pee);
}
$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
$pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
if (strpos($pee, '<pre') !== false)
$pee = preg_replace('!(<pre.*?>)(.*?)</pre>!ise', " stripslashes('$1') . stripslashes(clean_pre('$2')) . '</pre>' ", $pee);
$pee = preg_replace( "|\n</p>$|", '</p>', $pee );
return $pee;
}
function seems_utf8($Str) { # by bmorel at ssi dot fr
for ($i=0; $i<strlen($Str); $i++) {
if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb
elseif ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == strlen($Str)) || ((ord($Str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
function specialchars( $text, $quotes = 0 ) {
// Like htmlspecialchars except don't double-encode HTML entities
$text = str_replace('&&', '&#038;&', $text);
$text = str_replace('&&', '&#038;&', $text);
$text = preg_replace('/&(?:$|([^#])(?![a-z1-4]{1,8};))/', '&#038;$1', $text);
$text = str_replace('<', '&lt;', $text);
$text = str_replace('>', '&gt;', $text);
if ( 'double' === $quotes ) {
$text = str_replace('"', '&quot;', $text);
} elseif ( 'single' === $quotes ) {
$text = str_replace("'", '&#039;', $text);
} elseif ( $quotes ) {
$text = str_replace('"', '&quot;', $text);
$text = str_replace("'", '&#039;', $text);
}
return $text;
}
function utf8_uri_encode( $utf8_string, $length = 0 ) {
$unicode = '';
$values = array();
$num_octets = 1;
for ($i = 0; $i < strlen( $utf8_string ); $i++ ) {
$value = ord( $utf8_string[ $i ] );
if ( $value < 128 ) {
if ( $length && ( strlen($unicode) + 1 > $length ) )
break;
$unicode .= chr($value);
} else {
if ( count( $values ) == 0 ) $num_octets = ( $value < 224 ) ? 2 : 3;
$values[] = $value;
if ( $length && ( (strlen($unicode) + ($num_octets * 3)) > $length ) )
break;
if ( count( $values ) == $num_octets ) {
if ($num_octets == 3) {
$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]) . '%' . dechex($values[2]);
} else {
$unicode .= '%' . dechex($values[0]) . '%' . dechex($values[1]);
}
$values = array();
$num_octets = 1;
}
}
}
return $unicode;
}
function remove_accents($string) {
if ( !preg_match('/[\x80-\xff]/', $string) )
return $string;
if (seems_utf8($string)) {
$chars = array(
// Decompositions for Latin-1 Supplement
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
chr(195).chr(191) => 'y',
// Decompositions for Latin Extended-A
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
// Euro Sign
chr(226).chr(130).chr(172) => 'E',
// GBP (Pound) Sign
chr(194).chr(163) => '');
$string = strtr($string, $chars);
} else {
// Assume ISO-8859-1 if not UTF-8
$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
.chr(252).chr(253).chr(255);
$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
$string = strtr($string, $chars['in'], $chars['out']);
$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
$string = str_replace($double_chars['in'], $double_chars['out'], $string);
}
return $string;
}
function addslashes_gpc($gpc) {
global $db;
if (get_magic_quotes_gpc()) {
$gpc = stripslashes($gpc);
}
return $db->escape($gpc);
}
function stripslashes_deep($value) {
$value = is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);
return $value;
}
function urlencode_deep($value) {
$value = is_array($value) ?
array_map('urlencode_deep', $value) :
urlencode($value);
return $value;
}
function make_clickable($text) { // make all links within some text clickable; linkify
global $db;
$text = ' ' . $text;
// urls starting with http
$text = preg_replace_callback(
'#([\s>])(https?://[\w\#$%&~/.\-;:=,?!@\[\]+\(\)]*)#is',
function($m) {
return $m[1] . '<a href="' . brandit($m[2]) . '" >' . $m[2] . '</a>';
},
$text
);
// probable links starting with www or ftp
$text = preg_replace_callback(
'#([\s>])((www|ftp)\.[\w\#$%&~/.\-;:=,?!@\[\]+]*)#is',
function($m) {
return $m[1] . '<a href="' . brandit('https://' . $m[2]) . '" >' . $m[2] . '</a>';
},
$text
);
// hashtags
$text = preg_replace_callback(
'|([\s>])(#([a-z][a-z0-9_]{0,31}))|i',
function($m) {
return $m[1] . '<a href="/topics/' . $m[3] . '" >#' . $m[3] . '</a>';
},
$text
);
// mailtos
$text = preg_replace_callback(
'#([\s>])([a-z0-9\-_.]+)@([^,< \n\r]+)#i',
function($m) {
$email = $m[2] . '@' . $m[3];
return $m[1] . "<a href=\"mailto:$email\" >$email</a>";
},
$text
);
// this one needs to run last, for cleanup of accidental links within links
$text = preg_replace("#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $text);
$text = trim($text);
return $text;
}
function convertYoutube($string) {
return preg_replace(
"/(?:^|\s)[a-zA-Z\/\/:\.]*youtu(be.com\/watch\?v=|.be\/|be.com\/v\/|be.com\/embed\/)([a-zA-Z0-9\-_]+)([a-zA-Z0-9\/\*\-\_\?\&\;\%\=\.]*)/i",
"<iframe width=\"500\" height=\"375\" src=\"//www.youtube.com/embed/$2$3\" allowfullscreen></iframe>",
$string
);
}
function convertVimeo($string) {
return preg_replace(
"/(?:^|\s)[a-zA-Z\/\/:\.]*(player.)?vimeo.com\/(video\/)?([a-zA-Z0-9]+)/i",
"<iframe src=\"//player.vimeo.com/video/$3\" width=\"500\" height=\"375\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>",
$string
);
}
function imagify($ret) { // Inserts images if image link input, with correct width and height.
$ret = ' ' . $ret; // So that there will be an initial space and we can detect urls surrounded by space.
$ret = convertVimeo($ret);
$ret = convertYoutube($ret);
while (preg_match('#([\s>])((https?://[\w$%&~/.\-;:=,?@\[\]+]*?)\.(jpg|jpeg|gif|gifv|png|bmp))#is', $ret, $matches)) {
$pre = $matches[1];
$img_url = $matches[2];
$sans_suffix = $matches[3];
$suffix = $matches[4];
// get the image and put it in upload_dir
$filename = strtolower(preg_replace('|\W+|', '_', basename($sans_suffix))); // \w chars only, and dot
$filename = trim($filename, '_') . '.' . $suffix; // remove leading and trailing underscores, append suffix incl period
$oldname = "/tmp/$filename";
file_put_contents($oldname, file_get_contents($img_url, NULL, NULL, 0, 5000000));
$img_path = clean_and_move($filename, $oldname);
list ($new_image, $img_width, $img_height) = create_image($img_path);
// $new_image will be a full fs path. remove doc root (ABSPATH) from that to get url path.
$new_image = str_replace(ABSPATH, 'https://patrick.net/', $new_image);
$ret = preg_replace("#([\s>])($img_url)\S*#is", '$1<a href="' . $new_image . '"><img src="'. $new_image .'" width="' . $img_width . '" height="' . $img_height . '" ></a>', $ret);
// \S* is to delete trailing parameters from image
if (++$cycle >= 3) break; // Don't insert more than 3 images in a single post or comment.
}
$ret = trim($ret);
return $ret;
}
function is_email($email) {
$chars = "/^([a-z0-9+_]|\\-|\\.)+@(([a-z0-9_]|\\-)+\\.)+[a-z]{2,6}\$/i";
if (strpos($email, '@') !== false && strpos($email, '.') !== false) {
if (preg_match($chars, $email)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
function sanitize_email($email) {
$email = strip_tags($email);
$email = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '', $email);
$email = preg_replace('/&.+?;/', '', $email);
$email = preg_replace('/[^a-z0-9+_.@-]/i', '', $email);
return trim($email);
}
function ent2ncr($text) {
$to_ncr = array(
'&quot;' => '&#34;',
'&amp;' => '&#38;',
'&frasl;' => '&#47;',
'&lt;' => '&#60;',
'&gt;' => '&#62;',
'|' => '&#124;',
'&nbsp;' => '&#160;',
'&iexcl;' => '&#161;',
'&cent;' => '&#162;',
'&pound;' => '&#163;',
'&curren;' => '&#164;',
'&yen;' => '&#165;',
'&brvbar;' => '&#166;',
'&brkbar;' => '&#166;',
'&sect;' => '&#167;',
'&uml;' => '&#168;',
'&die;' => '&#168;',
'&copy;' => '&#169;',
'&ordf;' => '&#170;',
'&laquo;' => '&#171;',
'&not;' => '&#172;',
'&shy;' => '&#173;',
'&reg;' => '&#174;',
'&macr;' => '&#175;',
'&hibar;' => '&#175;',
'&deg;' => '&#176;',
'&plusmn;' => '&#177;',
'&sup2;' => '&#178;',
'&sup3;' => '&#179;',
'&acute;' => '&#180;',
'&micro;' => '&#181;',
'&para;' => '&#182;',
'&middot;' => '&#183;',
'&cedil;' => '&#184;',
'&sup1;' => '&#185;',
'&ordm;' => '&#186;',
'&raquo;' => '&#187;',
'&frac14;' => '&#188;',
'&frac12;' => '&#189;',
'&frac34;' => '&#190;',
'&iquest;' => '&#191;',
'&Agrave;' => '&#192;',
'&Aacute;' => '&#193;',
'&Acirc;' => '&#194;',
'&Atilde;' => '&#195;',
'&Auml;' => '&#196;',
'&Aring;' => '&#197;',
'&AElig;' => '&#198;',
'&Ccedil;' => '&#199;',
'&Egrave;' => '&#200;',
'&Eacute;' => '&#201;',
'&Ecirc;' => '&#202;',
'&Euml;' => '&#203;',
'&Igrave;' => '&#204;',
'&Iacute;' => '&#205;',
'&Icirc;' => '&#206;',
'&Iuml;' => '&#207;',
'&ETH;' => '&#208;',
'&Ntilde;' => '&#209;',
'&Ograve;' => '&#210;',
'&Oacute;' => '&#211;',
'&Ocirc;' => '&#212;',
'&Otilde;' => '&#213;',
'&Ouml;' => '&#214;',
'&times;' => '&#215;',
'&Oslash;' => '&#216;',
'&Ugrave;' => '&#217;',
'&Uacute;' => '&#218;',
'&Ucirc;' => '&#219;',
'&Uuml;' => '&#220;',
'&Yacute;' => '&#221;',
'&THORN;' => '&#222;',
'&szlig;' => '&#223;',
'&agrave;' => '&#224;',
'&aacute;' => '&#225;',
'&acirc;' => '&#226;',
'&atilde;' => '&#227;',
'&auml;' => '&#228;',
'&aring;' => '&#229;',
'&aelig;' => '&#230;',
'&ccedil;' => '&#231;',
'&egrave;' => '&#232;',
'&eacute;' => '&#233;',
'&ecirc;' => '&#234;',
'&euml;' => '&#235;',
'&igrave;' => '&#236;',
'&iacute;' => '&#237;',
'&icirc;' => '&#238;',
'&iuml;' => '&#239;',
'&eth;' => '&#240;',
'&ntilde;' => '&#241;',
'&ograve;' => '&#242;',
'&oacute;' => '&#243;',
'&ocirc;' => '&#244;',
'&otilde;' => '&#245;',
'&ouml;' => '&#246;',
'&divide;' => '&#247;',
'&oslash;' => '&#248;',
'&ugrave;' => '&#249;',
'&uacute;' => '&#250;',
'&ucirc;' => '&#251;',
'&uuml;' => '&#252;',
'&yacute;' => '&#253;',
'&thorn;' => '&#254;',
'&yuml;' => '&#255;',
'&OElig;' => '&#338;',
'&oelig;' => '&#339;',
'&Scaron;' => '&#352;',
'&scaron;' => '&#353;',
'&Yuml;' => '&#376;',
'&fnof;' => '&#402;',
'&circ;' => '&#710;',
'&tilde;' => '&#732;',
'&Alpha;' => '&#913;',
'&Beta;' => '&#914;',
'&Gamma;' => '&#915;',
'&Delta;' => '&#916;',
'&Epsilon;' => '&#917;',
'&Zeta;' => '&#918;',
'&Eta;' => '&#919;',
'&Theta;' => '&#920;',
'&Iota;' => '&#921;',
'&Kappa;' => '&#922;',
'&Lambda;' => '&#923;',
'&Mu;' => '&#924;',
'&Nu;' => '&#925;',
'&Xi;' => '&#926;',
'&Omicron;' => '&#927;',
'&Pi;' => '&#928;',
'&Rho;' => '&#929;',
'&Sigma;' => '&#931;',
'&Tau;' => '&#932;',
'&Upsilon;' => '&#933;',
'&Phi;' => '&#934;',
'&Chi;' => '&#935;',
'&Psi;' => '&#936;',
'&Omega;' => '&#937;',
'&alpha;' => '&#945;',
'&beta;' => '&#946;',
'&gamma;' => '&#947;',
'&delta;' => '&#948;',
'&epsilon;' => '&#949;',
'&zeta;' => '&#950;',
'&eta;' => '&#951;',
'&theta;' => '&#952;',
'&iota;' => '&#953;',
'&kappa;' => '&#954;',
'&lambda;' => '&#955;',
'&mu;' => '&#956;',
'&nu;' => '&#957;',
'&xi;' => '&#958;',
'&omicron;' => '&#959;',
'&pi;' => '&#960;',
'&rho;' => '&#961;',
'&sigmaf;' => '&#962;',
'&sigma;' => '&#963;',
'&tau;' => '&#964;',
'&upsilon;' => '&#965;',
'&phi;' => '&#966;',
'&chi;' => '&#967;',
'&psi;' => '&#968;',
'&omega;' => '&#969;',
'&thetasym;' => '&#977;',
'&upsih;' => '&#978;',
'&piv;' => '&#982;',
'&ensp;' => '&#8194;',
'&emsp;' => '&#8195;',
'&thinsp;' => '&#8201;',
'&zwnj;' => '&#8204;',
'&zwj;' => '&#8205;',
'&lrm;' => '&#8206;',
'&rlm;' => '&#8207;',
'&ndash;' => '&#8211;',
'&mdash;' => '&#8212;',
'&lsquo;' => '&#8216;',
'&rsquo;' => '&#8217;',
'&sbquo;' => '&#8218;',
'&ldquo;' => '&#8220;',
'&rdquo;' => '&#8221;',
'&bdquo;' => '&#8222;',
'&dagger;' => '&#8224;',
'&Dagger;' => '&#8225;',
'&bull;' => '&#8226;',
'&hellip;' => '&#8230;',
'&permil;' => '&#8240;',
'&prime;' => '&#8242;',
'&Prime;' => '&#8243;',
'&lsaquo;' => '&#8249;',
'&rsaquo;' => '&#8250;',
'&oline;' => '&#8254;',
'&frasl;' => '&#8260;',
'&euro;' => '&#8364;',
'&image;' => '&#8465;',
'&weierp;' => '&#8472;',
'&real;' => '&#8476;',
'&trade;' => '&#8482;',
'&alefsym;' => '&#8501;',
'&crarr;' => '&#8629;',
'&lArr;' => '&#8656;',
'&uArr;' => '&#8657;',
'&rArr;' => '&#8658;',
'&dArr;' => '&#8659;',
'&hArr;' => '&#8660;',
'&forall;' => '&#8704;',
'&part;' => '&#8706;',
'&exist;' => '&#8707;',
'&empty;' => '&#8709;',
'&nabla;' => '&#8711;',
'&isin;' => '&#8712;',
'&notin;' => '&#8713;',
'&ni;' => '&#8715;',
'&prod;' => '&#8719;',
'&sum;' => '&#8721;',
'&minus;' => '&#8722;',
'&lowast;' => '&#8727;',
'&radic;' => '&#8730;',
'&prop;' => '&#8733;',
'&infin;' => '&#8734;',
'&ang;' => '&#8736;',
'&and;' => '&#8743;',
'&or;' => '&#8744;',
'&cap;' => '&#8745;',
'&cup;' => '&#8746;',
'&int;' => '&#8747;',
'&there4;' => '&#8756;',
'&sim;' => '&#8764;',
'&cong;' => '&#8773;',
'&asymp;' => '&#8776;',
'&ne;' => '&#8800;',
'&equiv;' => '&#8801;',
'&le;' => '&#8804;',
'&ge;' => '&#8805;',
'&sub;' => '&#8834;',
'&sup;' => '&#8835;',
'&nsub;' => '&#8836;',
'&sube;' => '&#8838;',
'&supe;' => '&#8839;',
'&oplus;' => '&#8853;',
'&otimes;' => '&#8855;',
'&perp;' => '&#8869;',
'&sdot;' => '&#8901;',
'&lceil;' => '&#8968;',
'&rceil;' => '&#8969;',
'&lfloor;' => '&#8970;',
'&rfloor;' => '&#8971;',
'&lang;' => '&#9001;',
'&rang;' => '&#9002;',
'&larr;' => '&#8592;',
'&uarr;' => '&#8593;',
'&rarr;' => '&#8594;',
'&darr;' => '&#8595;',
'&harr;' => '&#8596;',
'&loz;' => '&#9674;',
'&spades;' => '&#9824;',
'&clubs;' => '&#9827;',
'&hearts;' => '&#9829;',
'&diams;' => '&#9830;'
);
return str_replace( array_keys($to_ncr), array_values($to_ncr), $text );
}
function clean_url( $url, $protocols = null ) {
if ('' == $url) return $url;
$url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%]|i', '', $url);
$strip = array('%0d', '%0a');
$url = str_replace($strip, '', $url);
$url = str_replace(';//', '://', $url);
// Append http unless a relative link starting with / or a php file.
if ( strpos($url, '://') === false && substr( $url, 0, 1 ) != '/' && !preg_match('/^[a-z0-9-]+?\.php/i', $url) )
$url = 'https://' . $url;
if ( !is_array($protocols) )
$protocols = array('http', 'https', 'mailto');
if ( kses_bad_protocol( $url, $protocols ) != $url ) return '';
return $url;
}
// Escaping for HTML attributes
function attribute_escape($text) {
$safe_text = specialchars($text, true);
return $safe_text;
}
function p_parse_str( $string, &$array ) {
parse_str( $string, $array );
if ( get_magic_quotes_gpc() )
$array = stripslashes_deep( $array ); // parse_str() adds slashes if magicquotes is on. See: https://php.net/parse_str
}
function get_external_link($html) { // get the first external link from some html
$doc = new DOMDocument();
@$doc->loadHTML($html);
$tags = $doc->getElementsByTagName('a');
foreach ($tags as $tag) {
$link = clean_url($tag->getAttribute('href'));
$parse = parse_url($link);
if ($parse['host'] && !preg_match('/patrick.net/', $parse['host'])) $link_array[] = $link;
}
return brandit($link_array[0]);
}
function get_first_image($html) { // get the first src image from some html
$doc = new DOMDocument();
@$doc->loadHTML($html);
$tags = $doc->getElementsByTagName('img');
foreach ($tags as $tag) $link_array[] = clean_url($tag->getAttribute('src'));
return $link_array[0];
}
function getlinks($string) {
$doc = new DOMDocument();
@$doc->loadHTML($string);
//$tags = $doc->getElementsByTagName('img');
//foreach ($tags as $tag) $link_array[] = $tag->getAttribute('src');
$tags = $doc->getElementsByTagName('a');
foreach ($tags as $tag) $link_array[] = clean_url($tag->getAttribute('href'));
if (count($link_array) > 0)
return array_unique($link_array);
else
return 0;
}
function comment_pagination($start, $end, $post, $page) {
$end = $post->post_comments > $start + 40 ? $start + 40 : $post->post_comments;
$from_one_start = $start + 1;
if ($start > 0) $previouspage = $page + 1;
if ($page > 0) $nextpage = $page - 1;
$maxpage = intval($post->post_comments / 40);
print "<p id='comments'>";
if ($start > 0) {
print "<a href='". post_id2path($post->post_id) ."?page=$maxpage#comments' title='Jump to first comment' >&laquo; First</a> &nbsp; &nbsp; ";
print "<a href='". post_id2path($post->post_id) ."?page=$previouspage#comments' title='Previous page of comments' >&laquo; Previous</a> &nbsp; &nbsp; ";
}
$s = $post->post_comments == 1 ? "" : "s";
print "Comment$s $from_one_start-$end of $post->post_comments &nbsp; &nbsp; ";
if ($page > 0) print "<a href='". post_id2path($post->post_id) ."?page=$nextpage#comments'>Next &raquo;</a> &nbsp; &nbsp; ";
print "<a href='". post_id2path($post->post_id) ."#comment-$post->post_latest_comment_id' title='Jump to last comment' >Last &raquo;</a></br>";
}
function post_comment_list($post_id) {
global $post, $db, $user_id, $comments, $comment, $current_user;
// If page is not set, show the 40 most recent comments. Same as page 0.
// If page is set, show that page, counting backwards from most recent.
// So page 1 would be the 40 comments before the most recent.
// If we were passed a 'c' parm, then calculate the page from that, overriding any page parm.
// This allows permalinks regardless of page number.
if ($c = intval(substr($_GET['c'], 0, 12))) {
// Get all the comments, sorted from greatest to least.
// What page of 40 from the end is our comment in? Start counting with page 0 being the most recent comments.
$page = $db->get_var("select floor(count(*)/40 - 0.01) from comments where comment_post_id=$post->post_id and comment_id >= $c order by comment_id");
}
$page = $page > 0 ? $page : intval(substr($_GET['page'], 0, 8)); // if page is not set from $c above, use $_GET['page']
$start = $post->post_comments - 40 * ($page + 1);
if ($start < 0) $start = 0;
// if this gets too slow as user_adhom_comments increases, try a left join, or just start deleting old adhom comments
$sql = "select SQL_CALC_FOUND_ROWS * from comments
where comment_post_id = '$post->post_id'
and comment_adhom_when is null
ORDER BY comment_date limit $start, 40";
$comments = $db->get_results($sql);
if ($comments) {
comment_pagination($start, $end, $post, $page);
comment_list($start, $post_id);
comment_pagination($start, $end, $post, $page);
}
?><p><?
if ($current_user->is_banned_by[$post->post_author])
print "You cannot comment here because the post author has banned you.";
else
comment_box($post);
}
function format_comment($comment, $num, $post_id, $upvoted=0, $downvoted=0) { // do not print formatted comment, just return it as a string
global $current_user;
$s = "<div class='comment' id='comment-$comment->comment_id' >";
$s .= "$num &nbsp; ";
if (get_post($post_id)->post_private)
$s .= icon($comment->comment_author, 0.5) . ' ' . user_realname($comment->comment_author) . ' &nbsp; ';
else
$s .= icon($comment->comment_author, 0.5) . ' ' . name_posts($comment->comment_author) . ' &nbsp; ';
if ( (get_post($post_id)->post_private) and
($comment->comment_author == $current_user->user_id) and
(!strlen($current_user->user_realname))
) {
$uemail = urlencode($current_user->user_email);
$s .= "<form action='/assign_names.php' method='GET' style='display:inline' ><input type='text' name='$current_user->user_id' size='20' placeholder='enter your real name' /><input type='hidden' name='post_id' value='$post_id' /></form>";
}
else $s .= show_civility($comment->comment_author);
$s .= ' &nbsp; ';
$n = get_userrow($comment->comment_author)->user_name;
$formatted_date = zdate($comment->comment_date);
$s .= "<a href='". post_id2path($comment->comment_post_id) ."?c=$comment->comment_id#comment-$comment->comment_id' title='permalink' >$formatted_date</a> &nbsp;";
$comment_likes = $comment->comment_likes ? "($comment->comment_likes)" : "";
$comment_dislikes = $comment->comment_dislikes ? "($comment->comment_dislikes)" : "";
if (is_user_logged_in()) {
$liketext = $upvoted ? 'you like this' : '&#8593;&nbsp;like';
$disliketext = $downvoted ? 'you dislike this' : '&#8595;&nbsp;dislike';
$s .= "<a HREF='#' id='like_$comment->comment_id' onclick=\"like('like_$comment->comment_id');return false\">$liketext $comment_likes</A> &nbsp; ";
$s .= "<a HREF='#' id='dislike_$comment->comment_id' onclick=\"dislike('dislike_$comment->comment_id');return false\">$disliketext $comment_dislikes</A> &nbsp; ";
if (preg_match('/jail/', $_SERVER['REQUEST_URI']) and (1 == $current_user->user_id)) {
$s .= " <a href='/liberate.php?comment_id=$comment->comment_id' >liberate</a> &nbsp; ";
}
elseif ($post_id and $current_user->user_pbias >= 3) {
if (!get_post($post_id)->post_private) {
$ts = time();
$nonce=get_nonce($ts);
$nonce_parms = "ts=$ts&nonce=$nonce";
$confirm_adhom = 'onClick="javascript:return confirm(\'Really mark as ad hominem?\')"';
$s .= " <a href='/adhominem.php?comment_id=$comment->comment_id&$nonce_parms' $confirm_adhom title='attacks person, not point' >ad hominem</a> &nbsp; ";
}
}
}
else { // not logged in. assume not registered, put link to reg page.
$s .= "<a HREF='/login.php?action=registerform'>&#8593;&nbsp;like $comment_likes</A> &nbsp; ";
$s .= "<a HREF='/login.php?action=registerform'>&#8595;&nbsp;dislike $comment_dislikes</A> &nbsp; ";
}
if ($post_id) {
$commenter = get_userrow($comment->comment_author);
$s .= "<a href=\"#commentform\" onclick=\"addquote('$comment->comment_post_id', '$comment->comment_id', '$commenter->user_name'); return false;\" title=\"Select some text then click this to quote\" >quote</a> &nbsp; ";
}
if ( ($current_user->user_id == $comment->comment_author) or ($current_user->user_level == 4) )
$s .= " <a href='/edit_comment.php?action=editcomment&c=$comment->comment_id'>edit</a> &nbsp; ";
if ($current_user->user_level == 4) { // only admin can delete comments
$ts = time();
$nonce=get_nonce($ts);
$nonce_parms = "ts=$ts&nonce=$nonce";
$confirm_del = 'onClick="javascript:return confirm(\'Really delete?\')"';
$s .= " <a href='/delete_comment.php?comment_id=$comment->comment_id&$nonce_parms' $confirm_del >delete</a> &nbsp; ";
}
$share_link = urlencode('https://patrick.net' . post_id2path($post_id) . "?c=$comment->comment_id#comment-$comment->comment_id");
$s .= "<a href='mailto:?subject=Patrick.net comment&body=$share_link' title='email this' ><img src='/images/mailicon.jpg' width=15 height=12 ></a> &nbsp;";
$s .= "<p><div id='comment-$comment->comment_id-text' >$comment->comment_content</div></div><p>";
return $s;
}
function send_comment_confirmation_mail($comment) {
global $db;
$u = get_userrow($comment->comment_author);
$key = substr(md5(microtime()), 0, 8);
$db->query("update users set user_activation_key='$key' where user_id=$u->user_id");
$comment_id = $comment->comment_id;
$redirect_to = post_id2path($comment->comment_post_id) . "#comment-$comment_id";
$uredirect_to = urlencode($redirect_to);
$umessage = "Click to confirm your comment on Patrick.net:<p>\r\n\r\n";
$umessage .= "<a href='https://patrick.net/login.php?action=confirm_comment&key=$key&redirect_to=$uredirect_to'>";
$umessage .= "https://patrick.net/login.php?action=confirm_comment&key=$key&redirect_to=$uredirect_to</a>\r\n\r\n";
format_mail($u->user_email, 'Confirm patrick.net comment', $umessage);
}
function comment_list($num = 0, $post_id = 0) { // Generic comment list, used in post_comment_list and best.php.
global $db, $user_id, $comments, $comment, $current_user, $post;
$current_user = get_user();
$post_latest_comment_id = end($comments)->comment_id; // To link directly to latest comment.
foreach ($comments as $comment) $commenters[] = $comment->comment_author;
$i_am_banned_by = banned_by($commenters);
list($my_up_votes, $my_down_votes) = select_my_comment_votes($comments);
foreach ($comments as $comment) $pida[] = $comment->comment_post_id;
$pidl = implode(',', $pida);
$privates = $db->get_results("select distinct whitelist_post_id from whitelists where whitelist_post_id in ($pidl)");
foreach ($privates as $private) $private_post[$private->post_id] = 1;
$permissions = $db->get_results("select whitelist_post_id from whitelists where whitelist_user_id=$current_user->user_id and whitelist_post_id in ($pidl)");
foreach ($permissions as $permit) $allowed[$permit->post_id] = 1;
$i_am_banned_by = banned_by($authors);
foreach ($comments as $comment) {
$num++; // always increment, even for moderated comments
$post_id = $comment->comment_post_id;
if ($private_post[$post_id] and !$allowed[$post_id]) continue; // Don't show comment from private post if user is not on its whitelist.
$warning = '';
if (!$comment->comment_approved) {
if ($comment->comment_author != intval($_GET['user_id'])) continue;
else {
send_comment_confirmation_mail($comment); // they personally will see the comment right away, but it will really be in moderation
}
}
print format_comment($comment, $num, $comment->comment_post_id, $my_up_votes[$comment->comment_id], $my_down_votes[$comment->comment_id]);
if ($warning) print $warning;
}
}
function comment_box($post) {
global $current_user, $user_id, $db;
?><div id="liveComments"></div><?