PageRenderTime 490ms CodeModel.GetById 101ms app.highlight 216ms RepoModel.GetById 156ms app.codeStats 1ms

/tags/2.6.3/wp-includes/comment.php

#
PHP | 1352 lines | 701 code | 164 blank | 487 comment | 161 complexity | 0174166451591a29388a9fffbd9b67af MD5 | raw file
   1<?php
   2/**
   3 * Manages WordPress comments
   4 *
   5 * @package WordPress
   6 */
   7
   8/**
   9 * Checks whether a comment passes internal checks to be allowed to add.
  10 *
  11 * If comment moderation is set in the administration, then all comments,
  12 * regardless of their type and whitelist will be set to false.
  13 *
  14 * If the number of links exceeds the amount in the administration, then the
  15 * check fails.
  16 *
  17 * If any of the parameter contents match the blacklist of words, then the check
  18 * fails.
  19 *
  20 * If the comment is a trackback and part of the blogroll, then the trackback is
  21 * automatically whitelisted. If the comment author was approved before, then
  22 * the comment is automatically whitelisted.
  23 *
  24 * If none of the checks fail, then the failback is to set the check to pass
  25 * (return true).
  26 *
  27 * @since 1.2
  28 * @uses $wpdb
  29 *
  30 * @param string $author Comment Author's name
  31 * @param string $email Comment Author's email
  32 * @param string $url Comment Author's URL
  33 * @param string $comment Comment contents
  34 * @param string $user_ip Comment Author's IP address
  35 * @param string $user_agent Comment Author's User Agent
  36 * @param string $comment_type Comment type, either user submitted comment,
  37 *		trackback, or pingback
  38 * @return bool Whether the checks passed (true) and the comments should be
  39 *		displayed or set to moderated
  40 */
  41function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) {
  42	global $wpdb;
  43
  44	if ( 1 == get_option('comment_moderation') )
  45		return false; // If moderation is set to manual
  46
  47	if ( preg_match_all("|(href\t*?=\t*?['\"]?)?(https?:)?//|i", $comment, $out) >= get_option('comment_max_links') )
  48		return false; // Check # of external links
  49
  50	$mod_keys = trim(get_option('moderation_keys'));
  51	if ( !empty($mod_keys) ) {
  52		$words = explode("\n", $mod_keys );
  53
  54		foreach ($words as $word) {
  55			$word = trim($word);
  56
  57			// Skip empty lines
  58			if ( empty($word) )
  59				continue;
  60
  61			// Do some escaping magic so that '#' chars in the
  62			// spam words don't break things:
  63			$word = preg_quote($word, '#');
  64
  65			$pattern = "#$word#i";
  66			if ( preg_match($pattern, $author) ) return false;
  67			if ( preg_match($pattern, $email) ) return false;
  68			if ( preg_match($pattern, $url) ) return false;
  69			if ( preg_match($pattern, $comment) ) return false;
  70			if ( preg_match($pattern, $user_ip) ) return false;
  71			if ( preg_match($pattern, $user_agent) ) return false;
  72		}
  73	}
  74
  75	// Comment whitelisting:
  76	if ( 1 == get_option('comment_whitelist')) {
  77		if ( 'trackback' == $comment_type || 'pingback' == $comment_type ) { // check if domain is in blogroll
  78			$uri = parse_url($url);
  79			$domain = $uri['host'];
  80			$uri = parse_url( get_option('home') );
  81			$home_domain = $uri['host'];
  82			if ( $wpdb->get_var($wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_url LIKE (%s) LIMIT 1", '%'.$domain.'%')) || $domain == $home_domain )
  83				return true;
  84			else
  85				return false;
  86		} elseif ( $author != '' && $email != '' ) {
  87			// expected_slashed ($author, $email)
  88			$ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1");
  89			if ( ( 1 == $ok_to_comment ) &&
  90				( empty($mod_keys) || false === strpos( $email, $mod_keys) ) )
  91					return true;
  92			else
  93				return false;
  94		} else {
  95			return false;
  96		}
  97	}
  98	return true;
  99}
 100
 101/**
 102 * Retrieve the approved comments for post $post_id.
 103 *
 104 * @since 2.0
 105 * @uses $wpdb
 106 *
 107 * @param int $post_id The ID of the post
 108 * @return array $comments The approved comments
 109 */
 110function get_approved_comments($post_id) {
 111	global $wpdb;
 112	return $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1' ORDER BY comment_date", $post_id));
 113}
 114
 115/**
 116 * Retrieves comment data given a comment ID or comment object.
 117 *
 118 * If an object is passed then the comment data will be cached and then returned
 119 * after being passed through a filter.
 120 *
 121 * If the comment is empty, then the global comment variable will be used, if it
 122 * is set.
 123 *
 124 * @since 2.0
 125 * @uses $wpdb
 126 *
 127 * @param object|string|int $comment Comment to retrieve.
 128 * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants
 129 * @return object|array|null Depends on $output value.
 130 */
 131function &get_comment(&$comment, $output = OBJECT) {
 132	global $wpdb;
 133
 134	if ( empty($comment) ) {
 135		if ( isset($GLOBALS['comment']) )
 136			$_comment = & $GLOBALS['comment'];
 137		else
 138			$_comment = null;
 139	} elseif ( is_object($comment) ) {
 140		wp_cache_add($comment->comment_ID, $comment, 'comment');
 141		$_comment = $comment;
 142	} else {
 143		if ( isset($GLOBALS['comment']) && ($GLOBALS['comment']->comment_ID == $comment) ) {
 144			$_comment = & $GLOBALS['comment'];
 145		} elseif ( ! $_comment = wp_cache_get($comment, 'comment') ) {
 146			$_comment = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment));
 147			wp_cache_add($_comment->comment_ID, $_comment, 'comment');
 148		}
 149	}
 150
 151	$_comment = apply_filters('get_comment', $_comment);
 152
 153	if ( $output == OBJECT ) {
 154		return $_comment;
 155	} elseif ( $output == ARRAY_A ) {
 156		return get_object_vars($_comment);
 157	} elseif ( $output == ARRAY_N ) {
 158		return array_values(get_object_vars($_comment));
 159	} else {
 160		return $_comment;
 161	}
 162}
 163
 164/**
 165 * Retrieve an array of comment data about comment $comment_ID.
 166 *
 167 * get_comment() technically does the same thing as this function. This function
 168 * also appears to reference variables and then not use them or not update them
 169 * when needed. It is advised to switch to get_comment(), since this function
 170 * might be deprecated in favor of using get_comment().
 171 *
 172 * @deprecated Use get_comment()
 173 * @see get_comment()
 174 * @since 0.71
 175 *
 176 * @uses $postc Comment cache, might not be used any more
 177 * @uses $id
 178 * @uses $wpdb Database Object
 179 *
 180 * @param int $comment_ID The ID of the comment
 181 * @param int $no_cache Whether to use the cache or not (casted to bool)
 182 * @param bool $include_unapproved Whether to include unapproved comments or not
 183 * @return array The comment data
 184 */
 185function get_commentdata( $comment_ID, $no_cache = 0, $include_unapproved = false ) {
 186	global $postc, $wpdb;
 187	if ( $no_cache ) {
 188		$query = $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d", $comment_ID);
 189		if ( false == $include_unapproved )
 190			$query .= " AND comment_approved = '1'";
 191		$myrow = $wpdb->get_row($query, ARRAY_A);
 192	} else {
 193		$myrow['comment_ID']           = $postc->comment_ID;
 194		$myrow['comment_post_ID']      = $postc->comment_post_ID;
 195		$myrow['comment_author']       = $postc->comment_author;
 196		$myrow['comment_author_email'] = $postc->comment_author_email;
 197		$myrow['comment_author_url']   = $postc->comment_author_url;
 198		$myrow['comment_author_IP']    = $postc->comment_author_IP;
 199		$myrow['comment_date']         = $postc->comment_date;
 200		$myrow['comment_content']      = $postc->comment_content;
 201		$myrow['comment_karma']        = $postc->comment_karma;
 202		$myrow['comment_approved']     = $postc->comment_approved;
 203		$myrow['comment_type']         = $postc->comment_type;
 204	}
 205	return $myrow;
 206}
 207
 208/**
 209 * The date the last comment was modified.
 210 *
 211 * {@internal Missing Long Description}}
 212 *
 213 * @since 1.5.0
 214 * @uses $wpdb
 215 * @global array $cache_lastcommentmodified
 216 *
 217 * @param string $timezone Which timezone to use in reference to 'gmt', 'blog',
 218 *		or 'server' locations
 219 * @return string Last comment modified date
 220 */
 221function get_lastcommentmodified($timezone = 'server') {
 222	global $cache_lastcommentmodified, $wpdb;
 223
 224	if ( isset($cache_lastcommentmodified[$timezone]) )
 225		return $cache_lastcommentmodified[$timezone];
 226
 227	$add_seconds_server = date('Z');
 228
 229	switch ( strtolower($timezone)) {
 230		case 'gmt':
 231			$lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
 232			break;
 233		case 'blog':
 234			$lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
 235			break;
 236		case 'server':
 237			$lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server));
 238			break;
 239	}
 240
 241	$cache_lastcommentmodified[$timezone] = $lastcommentmodified;
 242
 243	return $lastcommentmodified;
 244}
 245
 246/**
 247 * The amount of comments in a post or total comments.
 248 *
 249 * {@internal Missing Long Description}}
 250 *
 251 * @since 2.0.0
 252 * @uses $wpdb
 253 *
 254 * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide
 255 * @return array The amount of spam, approved, awaiting moderation, and total
 256 */
 257function get_comment_count( $post_id = 0 ) {
 258	global $wpdb;
 259
 260	$post_id = (int) $post_id;
 261
 262	$where = '';
 263	if ( $post_id > 0 ) {
 264		$where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id);
 265	}
 266
 267	$totals = (array) $wpdb->get_results("
 268		SELECT comment_approved, COUNT( * ) AS total
 269		FROM {$wpdb->comments}
 270		{$where}
 271		GROUP BY comment_approved
 272	", ARRAY_A);
 273
 274	$comment_count = array(
 275		"approved"              => 0,
 276		"awaiting_moderation"   => 0,
 277		"spam"                  => 0,
 278		"total_comments"        => 0
 279	);
 280
 281	foreach ( $totals as $row ) {
 282		switch ( $row['comment_approved'] ) {
 283			case 'spam':
 284				$comment_count['spam'] = $row['total'];
 285				$comment_count["total_comments"] += $row['total'];
 286				break;
 287			case 1:
 288				$comment_count['approved'] = $row['total'];
 289				$comment_count['total_comments'] += $row['total'];
 290				break;
 291			case 0:
 292				$comment_count['awaiting_moderation'] = $row['total'];
 293				$comment_count['total_comments'] += $row['total'];
 294				break;
 295			default:
 296				break;
 297		}
 298	}
 299
 300	return $comment_count;
 301}
 302
 303/**
 304 * Sanitizes the cookies sent to the user already.
 305 *
 306 * Will only do anything if the cookies have already been created for the user.
 307 * Mostly used after cookies had been sent to use elsewhere.
 308 *
 309 * @since 2.0.4
 310 */
 311function sanitize_comment_cookies() {
 312	if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) ) {
 313		$comment_author = apply_filters('pre_comment_author_name', $_COOKIE['comment_author_'.COOKIEHASH]);
 314		$comment_author = stripslashes($comment_author);
 315		$comment_author = attribute_escape($comment_author);
 316		$_COOKIE['comment_author_'.COOKIEHASH] = $comment_author;
 317	}
 318
 319	if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ) {
 320		$comment_author_email = apply_filters('pre_comment_author_email', $_COOKIE['comment_author_email_'.COOKIEHASH]);
 321		$comment_author_email = stripslashes($comment_author_email);
 322		$comment_author_email = attribute_escape($comment_author_email);
 323		$_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email;
 324	}
 325
 326	if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ) {
 327		$comment_author_url = apply_filters('pre_comment_author_url', $_COOKIE['comment_author_url_'.COOKIEHASH]);
 328		$comment_author_url = stripslashes($comment_author_url);
 329		$_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
 330	}
 331}
 332
 333/**
 334 * Validates whether this comment is allowed to be made or not.
 335 *
 336 * {@internal Missing Long Description}}
 337 *
 338 * @since 2.0.0
 339 * @uses $wpdb
 340 * @uses apply_filters() Calls 'pre_comment_approved' hook on the type of comment
 341 * @uses do_action() Calls 'check_comment_flood' hook on $comment_author_IP, $comment_author_email, and $comment_date_gmt
 342 *
 343 * @param array $commentdata Contains information on the comment
 344 * @return mixed Signifies the approval status (0|1|'spam')
 345 */
 346function wp_allow_comment($commentdata) {
 347	global $wpdb;
 348	extract($commentdata, EXTR_SKIP);
 349
 350	// Simple duplicate check
 351	// expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
 352	$dupe = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND ( comment_author = '$comment_author' ";
 353	if ( $comment_author_email )
 354		$dupe .= "OR comment_author_email = '$comment_author_email' ";
 355	$dupe .= ") AND comment_content = '$comment_content' LIMIT 1";
 356	if ( $wpdb->get_var($dupe) )
 357		wp_die( __('Duplicate comment detected; it looks as though you\'ve already said that!') );
 358
 359	do_action( 'check_comment_flood', $comment_author_IP, $comment_author_email, $comment_date_gmt );
 360
 361	if ( $user_id ) {
 362		$userdata = get_userdata($user_id);
 363		$user = new WP_User($user_id);
 364		$post_author = $wpdb->get_var($wpdb->prepare("SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1", $comment_post_ID));
 365	}
 366
 367	if ( $userdata && ( $user_id == $post_author || $user->has_cap('moderate_comments') ) ) {
 368		// The author and the admins get respect.
 369		$approved = 1;
 370	 } else {
 371		// Everyone else's comments will be checked.
 372		if ( check_comment($comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_IP, $comment_agent, $comment_type) )
 373			$approved = 1;
 374		else
 375			$approved = 0;
 376		if ( wp_blacklist_check($comment_author, $comment_author_email, $comment_author_url, $comment_content, $comment_author_IP, $comment_agent) )
 377			$approved = 'spam';
 378	}
 379
 380	$approved = apply_filters('pre_comment_approved', $approved);
 381	return $approved;
 382}
 383
 384/**
 385 * {@internal Missing Short Description}}
 386 *
 387 * {@internal Missing Long Description}}
 388 *
 389 * @since 2.3.0
 390 * @uses $wpdb
 391 * @uses apply_filters() {@internal Missing Description}}
 392 * @uses do_action() {@internal Missing Description}}
 393 *
 394 * @param string $ip {@internal Missing Description}}
 395 * @param string $email {@internal Missing Description}}
 396 * @param unknown_type $date {@internal Missing Description}}
 397 */
 398function check_comment_flood_db( $ip, $email, $date ) {
 399	global $wpdb;
 400	if ( current_user_can( 'manage_options' ) )
 401		return; // don't throttle admins
 402	if ( $lasttime = $wpdb->get_var( $wpdb->prepare("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_author_IP = %s OR comment_author_email = %s ORDER BY comment_date DESC LIMIT 1", $ip, $email) ) ) {
 403		$time_lastcomment = mysql2date('U', $lasttime);
 404		$time_newcomment  = mysql2date('U', $date);
 405		$flood_die = apply_filters('comment_flood_filter', false, $time_lastcomment, $time_newcomment);
 406		if ( $flood_die ) {
 407			do_action('comment_flood_trigger', $time_lastcomment, $time_newcomment);
 408			wp_die( __('You are posting comments too quickly.  Slow down.') );
 409		}
 410	}
 411}
 412
 413/**
 414 * Does comment contain blacklisted characters or words.
 415 *
 416 * {@internal Missing Long Description}}
 417 *
 418 * @since 1.5.0
 419 * @uses do_action() Calls 'wp_blacklist_check' hook for all parameters
 420 *
 421 * @param string $author The author of the comment
 422 * @param string $email The email of the comment
 423 * @param string $url The url used in the comment
 424 * @param string $comment The comment content
 425 * @param string $user_ip The comment author IP address
 426 * @param string $user_agent The author's browser user agent
 427 * @return bool True if comment contains blacklisted content, false if comment does not
 428 */
 429function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) {
 430	do_action('wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent);
 431
 432	if ( preg_match_all('/&#(\d+);/', $comment . $author . $url, $chars) ) {
 433		foreach ( (array) $chars[1] as $char ) {
 434			// If it's an encoded char in the normal ASCII set, reject
 435			if ( 38 == $char )
 436				continue; // Unless it's &
 437			if ( $char < 128 )
 438				return true;
 439		}
 440	}
 441
 442	$mod_keys = trim( get_option('blacklist_keys') );
 443	if ( '' == $mod_keys )
 444		return false; // If moderation keys are empty
 445	$words = explode("\n", $mod_keys );
 446
 447	foreach ( (array) $words as $word ) {
 448		$word = trim($word);
 449
 450		// Skip empty lines
 451		if ( empty($word) ) { continue; }
 452
 453		// Do some escaping magic so that '#' chars in the
 454		// spam words don't break things:
 455		$word = preg_quote($word, '#');
 456
 457		$pattern = "#$word#i";
 458		if (
 459			   preg_match($pattern, $author)
 460			|| preg_match($pattern, $email)
 461			|| preg_match($pattern, $url)
 462			|| preg_match($pattern, $comment)
 463			|| preg_match($pattern, $user_ip)
 464			|| preg_match($pattern, $user_agent)
 465		 )
 466			return true;
 467	}
 468	return false;
 469}
 470
 471/**
 472 * {@internal Missing Short Description}}
 473 *
 474 * {@internal Missing Long Description}}
 475 *
 476 * @param unknown_type $post_id
 477 * @return unknown
 478 */
 479function wp_count_comments( $post_id = 0 ) {
 480	global $wpdb;
 481
 482	$post_id = (int) $post_id;
 483
 484	$count = wp_cache_get("comments-{$post_id}", 'counts');
 485
 486	if ( false !== $count )
 487		return $count;
 488
 489	$where = '';
 490	if( $post_id > 0 )
 491		$where = $wpdb->prepare( "WHERE comment_post_ID = %d", $post_id );
 492
 493	$count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} {$where} GROUP BY comment_approved", ARRAY_A );
 494
 495	$total = 0;
 496	$stats = array( );
 497	$approved = array('0' => 'moderated', '1' => 'approved', 'spam' => 'spam');
 498	foreach( (array) $count as $row_num => $row ) {
 499		$total += $row['num_comments'];
 500		$stats[$approved[$row['comment_approved']]] = $row['num_comments'];
 501	}
 502
 503	$stats['total_comments'] = $total;
 504	foreach ( $approved as $key ) {
 505		if ( empty($stats[$key]) )
 506			$stats[$key] = 0;
 507	}
 508
 509	$stats = (object) $stats;
 510	wp_cache_set("comments-{$post_id}", $stats, 'counts');
 511
 512	return $stats;
 513}
 514
 515/**
 516 * Removes comment ID and maybe updates post comment count.
 517 *
 518 * The post comment count will be updated if the comment was approved and has a
 519 * post ID available.
 520 *
 521 * @since 2.0.0
 522 * @uses $wpdb
 523 * @uses do_action() Calls 'delete_comment' hook on comment ID
 524 * @uses do_action() Calls 'wp_set_comment_status' hook on comment ID with 'delete' set for the second parameter
 525 *
 526 * @param int $comment_id Comment ID
 527 * @return bool False if delete comment query failure, true on success
 528 */
 529function wp_delete_comment($comment_id) {
 530	global $wpdb;
 531	do_action('delete_comment', $comment_id);
 532
 533	$comment = get_comment($comment_id);
 534
 535	if ( ! $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment_id) ) )
 536		return false;
 537
 538	$post_id = $comment->comment_post_ID;
 539	if ( $post_id && $comment->comment_approved == 1 )
 540		wp_update_comment_count($post_id);
 541
 542	clean_comment_cache($comment_id);
 543
 544	do_action('wp_set_comment_status', $comment_id, 'delete');
 545	return true;
 546}
 547
 548/**
 549 * The status of a comment by ID.
 550 *
 551 * @since 1.0.0
 552 *
 553 * @param int $comment_id Comment ID
 554 * @return string|bool Status might be 'deleted', 'approved', 'unapproved', 'spam'. False on failure
 555 */
 556function wp_get_comment_status($comment_id) {
 557	$comment = get_comment($comment_id);
 558	if ( !$comment )
 559		return false;
 560
 561	$approved = $comment->comment_approved;
 562
 563	if ( $approved == NULL )
 564		return 'deleted';
 565	elseif ( $approved == '1' )
 566		return 'approved';
 567	elseif ( $approved == '0' )
 568		return 'unapproved';
 569	elseif ( $approved == 'spam' )
 570		return 'spam';
 571	else
 572		return false;
 573}
 574
 575/**
 576 * Get current commenter's name, email, and URL.
 577 *
 578 * Expects cookies content to already be sanitized. User of this function
 579 * might wish to recheck the returned array for validity.
 580 *
 581 * @see sanitize_comment_cookies() Use to sanitize cookies
 582 *
 583 * @since 2.0.4
 584 *
 585 * @return array Comment author, email, url respectively
 586 */
 587function wp_get_current_commenter() {
 588	// Cookies should already be sanitized.
 589
 590	$comment_author = '';
 591	if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) )
 592		$comment_author = $_COOKIE['comment_author_'.COOKIEHASH];
 593
 594	$comment_author_email = '';
 595	if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) )
 596		$comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH];
 597
 598	$comment_author_url = '';
 599	if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) )
 600		$comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH];
 601
 602	return compact('comment_author', 'comment_author_email', 'comment_author_url');
 603}
 604
 605/**
 606 * Inserts a comment to the database.
 607 *
 608 * {@internal Missing Long Description}}
 609 *
 610 * @since 2.0.0
 611 * @uses $wpdb
 612 *
 613 * @param array $commentdata Contains information on the comment
 614 * @return int The new comment's id
 615 */
 616function wp_insert_comment($commentdata) {
 617	global $wpdb;
 618	extract(stripslashes_deep($commentdata), EXTR_SKIP);
 619
 620	if ( ! isset($comment_author_IP) )
 621		$comment_author_IP = '';
 622	if ( ! isset($comment_date) )
 623		$comment_date = current_time('mysql');
 624	if ( ! isset($comment_date_gmt) )
 625		$comment_date_gmt = get_gmt_from_date($comment_date);
 626	if ( ! isset($comment_parent) )
 627		$comment_parent = 0;
 628	if ( ! isset($comment_approved) )
 629		$comment_approved = 1;
 630	if ( ! isset($user_id) )
 631		$user_id = 0;
 632
 633	$result = $wpdb->query( $wpdb->prepare("INSERT INTO $wpdb->comments
 634	(comment_post_ID, comment_author, comment_author_email, comment_author_url, comment_author_IP, comment_date, comment_date_gmt, comment_content, comment_approved, comment_agent, comment_type, comment_parent, user_id)
 635	VALUES (%d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %d)",
 636	$comment_post_ID, $comment_author, $comment_author_email, $comment_author_url, $comment_author_IP, $comment_date, $comment_date_gmt, $comment_content, $comment_approved, $comment_agent, $comment_type, $comment_parent, $user_id) );
 637
 638	$id = (int) $wpdb->insert_id;
 639
 640	if ( $comment_approved == 1)
 641		wp_update_comment_count($comment_post_ID);
 642
 643	return $id;
 644}
 645
 646/**
 647 * Parses and returns comment information.
 648 *
 649 * Sets the comment data 'filtered' field to true when finished. This can be
 650 * checked as to whether the comment should be filtered and to keep from
 651 * filtering the same comment more than once.
 652 *
 653 * @since 2.0.0
 654 * @uses apply_filters() Calls 'pre_user_id' hook on comment author's user ID
 655 * @uses apply_filters() Calls 'pre_comment_user_agent' hook on comment author's user agent
 656 * @uses apply_filters() Calls 'pre_comment_author_name' hook on comment author's name
 657 * @uses apply_filters() Calls 'pre_comment_content' hook on the comment's content
 658 * @uses apply_filters() Calls 'pre_comment_user_ip' hook on comment author's IP
 659 * @uses apply_filters() Calls 'pre_comment_author_url' hook on comment author's URL
 660 * @uses apply_filters() Calls 'pre_comment_author_email' hook on comment author's email address
 661 *
 662 * @param array $commentdata Contains information on the comment
 663 * @return array Parsed comment information
 664 */
 665function wp_filter_comment($commentdata) {
 666	$commentdata['user_id']              = apply_filters('pre_user_id', $commentdata['user_ID']);
 667	$commentdata['comment_agent']        = apply_filters('pre_comment_user_agent', $commentdata['comment_agent']);
 668	$commentdata['comment_author']       = apply_filters('pre_comment_author_name', $commentdata['comment_author']);
 669	$commentdata['comment_content']      = apply_filters('pre_comment_content', $commentdata['comment_content']);
 670	$commentdata['comment_author_IP']    = apply_filters('pre_comment_user_ip', $commentdata['comment_author_IP']);
 671	$commentdata['comment_author_url']   = apply_filters('pre_comment_author_url', $commentdata['comment_author_url']);
 672	$commentdata['comment_author_email'] = apply_filters('pre_comment_author_email', $commentdata['comment_author_email']);
 673	$commentdata['filtered'] = true;
 674	return $commentdata;
 675}
 676
 677/**
 678 * {@internal Missing Short Description}}
 679 *
 680 * {@internal Missing Long Description}}
 681 *
 682 * @since 2.1.0
 683 *
 684 * @param unknown_type $block {@internal Missing Description}}
 685 * @param unknown_type $time_lastcomment {@internal Missing Description}}
 686 * @param unknown_type $time_newcomment {@internal Missing Description}}
 687 * @return unknown {@internal Missing Description}}
 688 */
 689function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) {
 690	if ( $block ) // a plugin has already blocked... we'll let that decision stand
 691		return $block;
 692	if ( ($time_newcomment - $time_lastcomment) < 15 )
 693		return true;
 694	return false;
 695}
 696
 697/**
 698 * Parses and adds a new comment to the database.
 699 *
 700 * {@internal Missing Long Description}}
 701 *
 702 * @since 1.5.0
 703 * @uses apply_filters() Calls 'preprocess_comment' hook on $commentdata parameter array before processing
 704 * @uses do_action() Calls 'comment_post' hook on $comment_ID returned from adding the comment and if the comment was approved.
 705 * @uses wp_filter_comment() Used to filter comment before adding comment
 706 * @uses wp_allow_comment() checks to see if comment is approved.
 707 * @uses wp_insert_comment() Does the actual comment insertion to the database
 708 *
 709 * @param array $commentdata Contains information on the comment
 710 * @return int The ID of the comment after adding.
 711 */
 712function wp_new_comment( $commentdata ) {
 713	$commentdata = apply_filters('preprocess_comment', $commentdata);
 714
 715	$commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
 716	$commentdata['user_ID']         = (int) $commentdata['user_ID'];
 717
 718	$commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '',$_SERVER['REMOTE_ADDR'] );
 719	$commentdata['comment_agent']     = $_SERVER['HTTP_USER_AGENT'];
 720
 721	$commentdata['comment_date']     = current_time('mysql');
 722	$commentdata['comment_date_gmt'] = current_time('mysql', 1);
 723
 724	$commentdata = wp_filter_comment($commentdata);
 725
 726	$commentdata['comment_approved'] = wp_allow_comment($commentdata);
 727
 728	$comment_ID = wp_insert_comment($commentdata);
 729
 730	do_action('comment_post', $comment_ID, $commentdata['comment_approved']);
 731
 732	if ( 'spam' !== $commentdata['comment_approved'] ) { // If it's spam save it silently for later crunching
 733		if ( '0' == $commentdata['comment_approved'] )
 734			wp_notify_moderator($comment_ID);
 735
 736		$post = &get_post($commentdata['comment_post_ID']); // Don't notify if it's your own comment
 737
 738		if ( get_option('comments_notify') && $commentdata['comment_approved'] && $post->post_author != $commentdata['user_ID'] )
 739			wp_notify_postauthor($comment_ID, $commentdata['comment_type']);
 740	}
 741
 742	return $comment_ID;
 743}
 744
 745/**
 746 * Sets the status of comment ID.
 747 *
 748 * {@internal Missing Long Description}}
 749 *
 750 * @since 1.0.0
 751 *
 752 * @param int $comment_id Comment ID
 753 * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'delete'
 754 * @return bool False on failure or deletion and true on success.
 755 */
 756function wp_set_comment_status($comment_id, $comment_status) {
 757	global $wpdb;
 758
 759	switch ( $comment_status ) {
 760		case 'hold':
 761			$query = $wpdb->prepare("UPDATE $wpdb->comments SET comment_approved='0' WHERE comment_ID = %d LIMIT 1", $comment_id);
 762			break;
 763		case 'approve':
 764			$query = $wpdb->prepare("UPDATE $wpdb->comments SET comment_approved='1' WHERE comment_ID = %d LIMIT 1", $comment_id);
 765			if ( get_option('comments_notify') ) {
 766				$comment = get_comment($comment_id);
 767				wp_notify_postauthor($comment_id, $comment->comment_type);
 768			}
 769			break;
 770		case 'spam':
 771			$query = $wpdb->prepare("UPDATE $wpdb->comments SET comment_approved='spam' WHERE comment_ID = %d LIMIT 1", $comment_id);
 772			break;
 773		case 'delete':
 774			return wp_delete_comment($comment_id);
 775			break;
 776		default:
 777			return false;
 778	}
 779
 780	if ( !$wpdb->query($query) )
 781		return false;
 782
 783	clean_comment_cache($comment_id);
 784
 785	do_action('wp_set_comment_status', $comment_id, $comment_status);
 786	$comment = get_comment($comment_id);
 787	wp_update_comment_count($comment->comment_post_ID);
 788
 789	return true;
 790}
 791
 792/**
 793 * Parses and updates an existing comment in the database.
 794 *
 795 * {@internal Missing Long Description}}
 796 *
 797 * @since 2.0.0
 798 * @uses $wpdb
 799 *
 800 * @param array $commentarr Contains information on the comment
 801 * @return int Comment was updated if value is 1, or was not updated if value is 0.
 802 */
 803function wp_update_comment($commentarr) {
 804	global $wpdb;
 805
 806	// First, get all of the original fields
 807	$comment = get_comment($commentarr['comment_ID'], ARRAY_A);
 808
 809	// Escape data pulled from DB.
 810	foreach ( (array) $comment as $key => $value )
 811		$comment[$key] = $wpdb->escape($value);
 812
 813	// Merge old and new fields with new fields overwriting old ones.
 814	$commentarr = array_merge($comment, $commentarr);
 815
 816	$commentarr = wp_filter_comment( $commentarr );
 817
 818	// Now extract the merged array.
 819	extract(stripslashes_deep($commentarr), EXTR_SKIP);
 820
 821	$comment_content = apply_filters('comment_save_pre', $comment_content);
 822
 823	$comment_date_gmt = get_gmt_from_date($comment_date);
 824
 825	$wpdb->query( $wpdb->prepare("UPDATE $wpdb->comments SET
 826			comment_content      = %s,
 827			comment_author       = %s,
 828			comment_author_email = %s,
 829			comment_approved     = %s,
 830			comment_author_url   = %s,
 831			comment_date         = %s,
 832			comment_date_gmt     = %s
 833		WHERE comment_ID = %d",
 834			$comment_content,
 835			$comment_author,
 836			$comment_author_email,
 837			$comment_approved,
 838			$comment_author_url,
 839			$comment_date,
 840			$comment_date_gmt,
 841			$comment_ID) );
 842
 843	$rval = $wpdb->rows_affected;
 844
 845	clean_comment_cache($comment_ID);
 846	wp_update_comment_count($comment_post_ID);
 847	do_action('edit_comment', $comment_ID);
 848	return $rval;
 849}
 850
 851/**
 852 * Whether to defer comment counting.
 853 *
 854 * When setting $defer to true, all post comment counts will not be updated
 855 * until $defer is set to false. When $defer is set to false, then all
 856 * previously deferred updated post comment counts will then be automatically
 857 * updated without having to call wp_update_comment_count() after.
 858 *
 859 * @since 2.5
 860 * @staticvar bool $_defer
 861 *
 862 * @param bool $defer
 863 * @return unknown
 864 */
 865function wp_defer_comment_counting($defer=null) {
 866	static $_defer = false;
 867
 868	if ( is_bool($defer) ) {
 869		$_defer = $defer;
 870		// flush any deferred counts
 871		if ( !$defer )
 872			wp_update_comment_count( null, true );
 873	}
 874
 875	return $_defer;
 876}
 877
 878/**
 879 * Updates the comment count for post(s).
 880 *
 881 * When $do_deferred is false (is by default) and the comments have been set to
 882 * be deferred, the post_id will be added to a queue, which will be updated at a
 883 * later date and only updated once per post ID.
 884 *
 885 * If the comments have not be set up to be deferred, then the post will be
 886 * updated. When $do_deferred is set to true, then all previous deferred post
 887 * IDs will be updated along with the current $post_id.
 888 *
 889 * @since 2.1.0
 890 * @see wp_update_comment_count_now() For what could cause a false return value
 891 *
 892 * @param int $post_id Post ID
 893 * @param bool $do_deferred Whether to process previously deferred post comment counts
 894 * @return bool True on success, false on failure
 895 */
 896function wp_update_comment_count($post_id, $do_deferred=false) {
 897	static $_deferred = array();
 898
 899	if ( $do_deferred ) {
 900		$_deferred = array_unique($_deferred);
 901		foreach ( $_deferred as $i => $_post_id ) {
 902			wp_update_comment_count_now($_post_id);
 903			unset( $_deferred[$i] ); /** @todo Move this outside of the foreach and reset $_deferred to an array instead */
 904		}
 905	}
 906
 907	if ( wp_defer_comment_counting() ) {
 908		$_deferred[] = $post_id;
 909		return true;
 910	}
 911	elseif ( $post_id ) {
 912		return wp_update_comment_count_now($post_id);
 913	}
 914
 915}
 916
 917/**
 918 * Updates the comment count for the post.
 919 *
 920 * @since 2.5
 921 * @uses $wpdb
 922 * @uses do_action() Calls 'wp_update_comment_count' hook on $post_id, $new, and $old
 923 * @uses do_action() Calls 'edit_posts' hook on $post_id and $post
 924 *
 925 * @param int $post_id Post ID
 926 * @return bool False on '0' $post_id or if post with ID does not exist. True on success.
 927 */
 928function wp_update_comment_count_now($post_id) {
 929	global $wpdb;
 930	$post_id = (int) $post_id;
 931	if ( !$post_id )
 932		return false;
 933	if ( !$post = get_post($post_id) )
 934		return false;
 935
 936	$old = (int) $post->comment_count;
 937	$new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id) );
 938	$wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET comment_count = %d WHERE ID = %d", $new, $post_id) );
 939
 940	if ( 'page' == $post->post_type )
 941		clean_page_cache( $post_id );
 942	else
 943		clean_post_cache( $post_id );
 944
 945	do_action('wp_update_comment_count', $post_id, $new, $old);
 946	do_action('edit_post', $post_id, $post);
 947
 948	return true;
 949}
 950
 951//
 952// Ping and trackback functions.
 953//
 954
 955/**
 956 * Finds a pingback server URI based on the given URL.
 957 *
 958 * {@internal Missing Long Description}}
 959 *
 960 * @since 1.5.0
 961 * @uses $wp_version
 962 *
 963 * @param string $url URL to ping
 964 * @param int $timeout_bytes Number of bytes to timeout at. Prevents big file downloads, default is 2048.
 965 * @return bool|string False on failure, string containing URI on success.
 966 */
 967function discover_pingback_server_uri($url, $timeout_bytes = 2048) {
 968	global $wp_version;
 969
 970	$byte_count = 0;
 971	$contents = '';
 972	$headers = '';
 973	$pingback_str_dquote = 'rel="pingback"';
 974	$pingback_str_squote = 'rel=\'pingback\'';
 975	$x_pingback_str = 'x-pingback: ';
 976
 977	extract(parse_url($url), EXTR_SKIP);
 978
 979	if ( !isset($host) ) // Not an URL. This should never happen.
 980		return false;
 981
 982	$path  = ( !isset($path) ) ? '/'          : $path;
 983	$path .= ( isset($query) ) ? '?' . $query : '';
 984	$port  = ( isset($port)  ) ? $port        : 80;
 985
 986	// Try to connect to the server at $host
 987	$fp = @fsockopen($host, $port, $errno, $errstr, 2);
 988	if ( !$fp ) // Couldn't open a connection to $host
 989		return false;
 990
 991	// Send the GET request
 992	$request = "GET $path HTTP/1.1\r\nHost: $host\r\nUser-Agent: WordPress/$wp_version \r\n\r\n";
 993	// ob_end_flush();
 994	fputs($fp, $request);
 995
 996	// Let's check for an X-Pingback header first
 997	while ( !feof($fp) ) {
 998		$line = fgets($fp, 512);
 999		if ( trim($line) == '' )
1000			break;
1001		$headers .= trim($line)."\n";
1002		$x_pingback_header_offset = strpos(strtolower($headers), $x_pingback_str);
1003		if ( $x_pingback_header_offset ) {
1004			// We got it!
1005			preg_match('#x-pingback: (.+)#is', $headers, $matches);
1006			$pingback_server_url = trim($matches[1]);
1007			return $pingback_server_url;
1008		}
1009		if ( strpos(strtolower($headers), 'content-type: ') ) {
1010			preg_match('#content-type: (.+)#is', $headers, $matches);
1011			$content_type = trim($matches[1]);
1012		}
1013	}
1014
1015	if ( preg_match('#(image|audio|video|model)/#is', $content_type) ) // Not an (x)html, sgml, or xml page, no use going further
1016		return false;
1017
1018	while ( !feof($fp) ) {
1019		$line = fgets($fp, 1024);
1020		$contents .= trim($line);
1021		$pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote);
1022		$pingback_link_offset_squote = strpos($contents, $pingback_str_squote);
1023		if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) {
1024			$quote = ($pingback_link_offset_dquote) ? '"' : '\'';
1025			$pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote;
1026			$pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset);
1027			$pingback_href_start = $pingback_href_pos+6;
1028			$pingback_href_end = @strpos($contents, $quote, $pingback_href_start);
1029			$pingback_server_url_len = $pingback_href_end - $pingback_href_start;
1030			$pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len);
1031			// We may find rel="pingback" but an incomplete pingback URL
1032			if ( $pingback_server_url_len > 0 ) { // We got it!
1033				fclose($fp);
1034				return $pingback_server_url;
1035			}
1036		}
1037		$byte_count += strlen($line);
1038		if ( $byte_count > $timeout_bytes ) {
1039			// It's no use going further, there probably isn't any pingback
1040			// server to find in this file. (Prevents loading large files.)
1041			fclose($fp);
1042			return false;
1043		}
1044	}
1045
1046	// We didn't find anything.
1047	fclose($fp);
1048	return false;
1049}
1050
1051/**
1052 * {@internal Missing Short Description}}
1053 *
1054 * {@internal Missing Long Description}}
1055 *
1056 * @since 2.1.0
1057 * @uses $wpdb
1058 */
1059function do_all_pings() {
1060	global $wpdb;
1061
1062	// Do pingbacks
1063	while ($ping = $wpdb->get_row("SELECT * FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pingme' LIMIT 1")) {
1064		$wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE post_id = {$ping->ID} AND meta_key = '_pingme';");
1065		pingback($ping->post_content, $ping->ID);
1066	}
1067
1068	// Do Enclosures
1069	while ($enclosure = $wpdb->get_row("SELECT * FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_encloseme' LIMIT 1")) {
1070		$wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_encloseme';", $enclosure->ID) );
1071		do_enclose($enclosure->post_content, $enclosure->ID);
1072	}
1073
1074	// Do Trackbacks
1075	$trackbacks = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE to_ping <> '' AND post_status = 'publish'");
1076	if ( is_array($trackbacks) )
1077		foreach ( $trackbacks as $trackback )
1078			do_trackbacks($trackback);
1079
1080	//Do Update Services/Generic Pings
1081	generic_ping();
1082}
1083
1084/**
1085 * {@internal Missing Short Description}}
1086 *
1087 * {@internal Missing Long Description}}
1088 *
1089 * @since 1.5.0
1090 * @uses $wpdb
1091 *
1092 * @param int $post_id Post ID to do trackbacks on
1093 */
1094function do_trackbacks($post_id) {
1095	global $wpdb;
1096
1097	$post = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id) );
1098	$to_ping = get_to_ping($post_id);
1099	$pinged  = get_pung($post_id);
1100	if ( empty($to_ping) ) {
1101		$wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = '' WHERE ID = %d", $post_id) );
1102		return;
1103	}
1104
1105	if ( empty($post->post_excerpt) )
1106		$excerpt = apply_filters('the_content', $post->post_content);
1107	else
1108		$excerpt = apply_filters('the_excerpt', $post->post_excerpt);
1109	$excerpt = str_replace(']]>', ']]&gt;', $excerpt);
1110	$excerpt = wp_html_excerpt($excerpt, 252) . '...';
1111
1112	$post_title = apply_filters('the_title', $post->post_title);
1113	$post_title = strip_tags($post_title);
1114
1115	if ( $to_ping ) {
1116		foreach ( (array) $to_ping as $tb_ping ) {
1117			$tb_ping = trim($tb_ping);
1118			if ( !in_array($tb_ping, $pinged) ) {
1119				trackback($tb_ping, $post_title, $excerpt, $post_id);
1120				$pinged[] = $tb_ping;
1121			} else {
1122				$wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_ping', '')) WHERE ID = %d", $post_id) );
1123			}
1124		}
1125	}
1126}
1127
1128/**
1129 * {@internal Missing Short Description}}
1130 *
1131 * {@internal Missing Long Description}}
1132 *
1133 * @since 1.2.0
1134 *
1135 * @param int $post_id Post ID. Not actually used.
1136 * @return int Same as Post ID from parameter
1137 */
1138function generic_ping($post_id = 0) {
1139	$services = get_option('ping_sites');
1140
1141	$services = explode("\n", $services);
1142	foreach ( (array) $services as $service ) {
1143		$service = trim($service);
1144		if ( '' != $service )
1145			weblog_ping($service);
1146	}
1147
1148	return $post_id;
1149}
1150
1151/**
1152 * Pings back the links found in a post.
1153 *
1154 * {@internal Missing Long Description}}
1155 *
1156 * @since 0.71
1157 * @uses $wp_version
1158 * @uses IXR_Client
1159 *
1160 * @param string $content {@internal Missing Description}}
1161 * @param int $post_ID {@internal Missing Description}}
1162 */
1163function pingback($content, $post_ID) {
1164	global $wp_version;
1165	include_once(ABSPATH . WPINC . '/class-IXR.php');
1166
1167	// original code by Mort (http://mort.mine.nu:8080)
1168	$post_links = array();
1169
1170	$pung = get_pung($post_ID);
1171
1172	// Variables
1173	$ltrs = '\w';
1174	$gunk = '/#~:.?+=&%@!\-';
1175	$punc = '.:?\-';
1176	$any = $ltrs . $gunk . $punc;
1177
1178	// Step 1
1179	// Parsing the post, external links (if any) are stored in the $post_links array
1180	// This regexp comes straight from phpfreaks.com
1181	// http://www.phpfreaks.com/quickcode/Extract_All_URLs_on_a_Page/15.php
1182	preg_match_all("{\b http : [$any] +? (?= [$punc] * [^$any] | $)}x", $content, $post_links_temp);
1183
1184	// Step 2.
1185	// Walking thru the links array
1186	// first we get rid of links pointing to sites, not to specific files
1187	// Example:
1188	// http://dummy-weblog.org
1189	// http://dummy-weblog.org/
1190	// http://dummy-weblog.org/post.php
1191	// We don't wanna ping first and second types, even if they have a valid <link/>
1192
1193	foreach ( $post_links_temp[0] as $link_test ) :
1194		if ( !in_array($link_test, $pung) && (url_to_postid($link_test) != $post_ID) // If we haven't pung it already and it isn't a link to itself
1195				&& !is_local_attachment($link_test) ) : // Also, let's never ping local attachments.
1196			$test = parse_url($link_test);
1197			if ( isset($test['query']) )
1198				$post_links[] = $link_test;
1199			elseif ( ($test['path'] != '/') && ($test['path'] != '') )
1200				$post_links[] = $link_test;
1201		endif;
1202	endforeach;
1203
1204	do_action_ref_array('pre_ping', array(&$post_links, &$pung));
1205
1206	foreach ( (array) $post_links as $pagelinkedto ) {
1207		$pingback_server_url = discover_pingback_server_uri($pagelinkedto, 2048);
1208
1209		if ( $pingback_server_url ) {
1210			@ set_time_limit( 60 );
1211			 // Now, the RPC call
1212			$pagelinkedfrom = get_permalink($post_ID);
1213
1214			// using a timeout of 3 seconds should be enough to cover slow servers
1215			$client = new IXR_Client($pingback_server_url);
1216			$client->timeout = 3;
1217			$client->useragent .= ' -- WordPress/' . $wp_version;
1218
1219			// when set to true, this outputs debug messages by itself
1220			$client->debug = false;
1221
1222			if ( $client->query('pingback.ping', $pagelinkedfrom, $pagelinkedto) || ( isset($client->error->code) && 48 == $client->error->code ) ) // Already registered
1223				add_ping( $post_ID, $pagelinkedto );
1224		}
1225	}
1226}
1227
1228/**
1229 * {@internal Missing Short Description}}
1230 *
1231 * {@internal Missing Long Description}}
1232 *
1233 * @since 2.1.0
1234 *
1235 * @param unknown_type $sites {@internal Missing Description}}
1236 * @return unknown {@internal Missing Description}}
1237 */
1238function privacy_ping_filter($sites) {
1239	if ( '0' != get_option('blog_public') )
1240		return $sites;
1241	else
1242		return '';
1243}
1244
1245/**
1246 * Send a Trackback.
1247 *
1248 * Updates database when sending trackback to prevent duplicates.
1249 *
1250 * @since 0.71
1251 * @uses $wpdb
1252 * @uses $wp_version WordPress version
1253 *
1254 * @param string $trackback_url URL to send trackbacks.
1255 * @param string $title Title of post
1256 * @param string $excerpt Excerpt of post
1257 * @param int $ID Post ID
1258 * @return mixed Database query from update
1259 */
1260function trackback($trackback_url, $title, $excerpt, $ID) {
1261	global $wpdb, $wp_version;
1262
1263	if ( empty($trackback_url) )
1264		return;
1265
1266	$title = urlencode($title);
1267	$excerpt = urlencode($excerpt);
1268	$blog_name = urlencode(get_option('blogname'));
1269	$tb_url = $trackback_url;
1270	$url = urlencode(get_permalink($ID));
1271	$query_string = "title=$title&url=$url&blog_name=$blog_name&excerpt=$excerpt";
1272	$trackback_url = parse_url($trackback_url);
1273	$http_request = 'POST ' . $trackback_url['path'] . ($trackback_url['query'] ? '?'.$trackback_url['query'] : '') . " HTTP/1.0\r\n";
1274	$http_request .= 'Host: '.$trackback_url['host']."\r\n";
1275	$http_request .= 'Content-Type: application/x-www-form-urlencoded; charset='.get_option('blog_charset')."\r\n";
1276	$http_request .= 'Content-Length: '.strlen($query_string)."\r\n";
1277	$http_request .= "User-Agent: WordPress/" . $wp_version;
1278	$http_request .= "\r\n\r\n";
1279	$http_request .= $query_string;
1280	if ( '' == $trackback_url['port'] )
1281		$trackback_url['port'] = 80;
1282	$fs = @fsockopen($trackback_url['host'], $trackback_url['port'], $errno, $errstr, 4);
1283	@fputs($fs, $http_request);
1284	@fclose($fs);
1285
1286	$tb_url = addslashes( $tb_url );
1287	$wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', '$tb_url') WHERE ID = %d", $ID) );
1288	return $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_url', '')) WHERE ID = %d", $ID) );
1289}
1290
1291/**
1292 * Send a pingback.
1293 *
1294 * @since 1.2.0
1295 * @uses $wp_version
1296 * @uses IXR_Client
1297 *
1298 * @param string $server Host of blog to connect to.
1299 * @param string $path Path to send the ping.
1300 */
1301function weblog_ping($server = '', $path = '') {
1302	global $wp_version;
1303	include_once(ABSPATH . WPINC . '/class-IXR.php');
1304
1305	// using a timeout of 3 seconds should be enough to cover slow servers
1306	$client = new IXR_Client($server, ((!strlen(trim($path)) || ('/' == $path)) ? false : $path));
1307	$client->timeout = 3;
1308	$client->useragent .= ' -- WordPress/'.$wp_version;
1309
1310	// when set to true, this outputs debug messages by itself
1311	$client->debug = false;
1312	$home = trailingslashit( get_option('home') );
1313	if ( !$client->query('weblogUpdates.extendedPing', get_option('blogname'), $home, get_bloginfo('rss2_url') ) ) // then try a normal ping
1314		$client->query('weblogUpdates.ping', get_option('blogname'), $home);
1315}
1316
1317//
1318// Cache
1319//
1320
1321/**
1322 * Removes comment ID from the comment cache.
1323 *
1324 * @since 2.3.0
1325 * @package WordPress
1326 * @subpackage Cache
1327 *
1328 * @param int $id Comment ID to remove from cache
1329 */
1330function clean_comment_cache($id) {
1331	wp_cache_delete($id, 'comment');
1332}
1333
1334/**
1335 * Updates the comment cache of given comments.
1336 *
1337 * Will add the comments in $comments to the cache. If comment ID already
1338 * exists in the comment cache then it will not be updated.
1339 *
1340 * The comment is added to the cache using the comment group with the key
1341 * using the ID of the comments.
1342 *
1343 * @since 2.3.0
1344 *
1345 * @param array $comments Array of comment row objects
1346 */
1347function update_comment_cache($comments) {
1348	foreach ( (array) $comments as $comment )
1349		wp_cache_add($comment->comment_ID, $comment, 'comment');
1350}
1351
1352?>