1<?php 2// +-----------------------------------------------------------------------+ 3// | This file is part of Piwigo. | 4// | | 5// | For copyright and license information, please view the COPYING.txt | 6// | file that was distributed with this source code. | 7// +-----------------------------------------------------------------------+ 8 9/** 10 * @package functions\comment 11 */ 12 13 14add_event_handler('user_comment_check', 'user_comment_check'); 15 16/** 17 * Does basic check on comment and returns action to perform. 18 * This method is called by a trigger_change() 19 * 20 * @param string $action before check 21 * @param array $comment 22 * @return string validate, moderate, reject 23 */ 24function user_comment_check($action, $comment) 25{ 26 global $conf,$user; 27 28 if ($action=='reject') 29 return $action; 30 31 $my_action = $conf['comment_spam_reject'] ? 'reject':'moderate'; 32 33 if ($action==$my_action) 34 return $action; 35 36 // we do here only BASIC spam check (plugins can do more) 37 if ( !is_a_guest() ) 38 return $action; 39 40 $link_count = preg_match_all( '/https?:\/\//', 41 $comment['content'], $matches); 42 43 if ( strpos($comment['author'], 'http://')!==false ) 44 { 45 $link_count++; 46 } 47 48 if ( $link_count>$conf['comment_spam_max_links'] ) 49 { 50 $_POST['cr'][] = 'links'; 51 return $my_action; 52 } 53 return $action; 54} 55 56/** 57 * Tries to insert a user comment and returns action to perform. 58 * 59 * @param array &$comm 60 * @param string $key secret key sent back to the browser 61 * @param array &$infos output array of error messages 62 * @return string validate, moderate, reject 63 */ 64function insert_user_comment(&$comm, $key, &$infos) 65{ 66 global $conf, $user; 67 68 $comm = array_merge( $comm, 69 array( 70 'ip' => $_SERVER['REMOTE_ADDR'], 71 'agent' => $_SERVER['HTTP_USER_AGENT'] 72 ) 73 ); 74 75 $infos = array(); 76 if (!$conf['comments_validation'] or is_admin()) 77 { 78 $comment_action='validate'; //one of validate, moderate, reject 79 } 80 else 81 { 82 $comment_action='moderate'; //one of validate, moderate, reject 83 } 84 85 // display author field if the user status is guest or generic 86 if (!is_classic_user()) 87 { 88 if ( empty($comm['author']) ) 89 { 90 if ($conf['comments_author_mandatory']) 91 { 92 $infos[] = l10n('Username is mandatory'); 93 $comment_action='reject'; 94 } 95 $comm['author'] = 'guest'; 96 } 97 $comm['author_id'] = $conf['guest_id']; 98 // if a guest try to use the name of an already existing user, he must be 99 // rejected 100 if ( $comm['author'] != 'guest' ) 101 { 102 $query = ' 103SELECT COUNT(*) AS user_exists 104 FROM '.USERS_TABLE.' 105 WHERE '.$conf['user_fields']['username']." = '".addslashes($comm['author'])."'"; 106 $row = pwg_db_fetch_assoc( pwg_query( $query ) ); 107 if ( $row['user_exists'] == 1 ) 108 { 109 $infos[] = l10n('This login is already used by another user'); 110 $comment_action='reject'; 111 } 112 } 113 } 114 else 115 { 116 $comm['author'] = addslashes($user['username']); 117 $comm['author_id'] = $user['id']; 118 } 119 120 if ( empty($comm['content']) ) 121 { // empty comment content 122 $comment_action='reject'; 123 } 124 125 if ( !verify_ephemeral_key(@$key, $comm['image_id']) ) 126 { 127 $comment_action='reject'; 128 $_POST['cr'][] = 'key'; // rvelices: I use this outside to see how spam robots work 129 } 130 131 // website 132 if (!empty($comm['website_url'])) 133 { 134 if (!$conf['comments_enable_website']) 135 { // honeypot: if the field is disabled, it should be empty ! 136 $comment_action='reject'; 137 $_POST['cr'][] = 'website_url'; 138 } 139 else 140 { 141 $comm['website_url'] = strip_tags($comm['website_url']); 142 if (!preg_match('/^https?/i', $comm['website_url'])) 143 { 144 $comm['website_url'] = 'http://'.$comm['website_url']; 145 } 146 if (!url_check_format($comm['website_url'])) 147 { 148 $infos[] = l10n('Your website URL is invalid'); 149 $comment_action='reject'; 150 } 151 } 152 } 153 154 // email 155 if (empty($comm['email'])) 156 { 157 if (!empty($user['email'])) 158 { 159 $comm['email'] = $user['email']; 160 } 161 elseif ($conf['comments_email_mandatory']) 162 { 163 $infos[] = l10n('Email address is missing. Please specify an email address.'); 164 $comment_action='reject'; 165 } 166 } 167 elseif (!email_check_format($comm['email'])) 168 { 169 $infos[] = l10n('mail address must be like xxx@yyy.eee (example : jack@altern.org)'); 170 $comment_action='reject'; 171 } 172 173 // anonymous id = ip address 174 $ip_components = explode('.', $comm['ip']); 175 if (count($ip_components) > 3) 176 { 177 array_pop($ip_components); 178 } 179 $anonymous_id = implode('.', $ip_components); 180 181 if ($comment_action!='reject' and $conf['anti-flood_time']>0 and !is_admin()) 182 { // anti-flood system 183 $reference_date = pwg_db_get_flood_period_expression($conf['anti-flood_time']); 184 185 $query = ' 186SELECT count(1) FROM '.COMMENTS_TABLE.' 187 WHERE date > '.$reference_date.' 188 AND author_id = '.$comm['author_id']; 189 if (!is_classic_user()) 190 { 191 $query.= ' 192 AND anonymous_id LIKE "'.$anonymous_id.'.%"'; 193 } 194 $query.= ' 195;'; 196 197 list($counter) = pwg_db_fetch_row(pwg_query($query)); 198 if ( $counter > 0 ) 199 { 200 $infos[] = l10n('Anti-flood system : please wait for a moment before trying to post another comment'); 201 $comment_action='reject'; 202 $_POST['cr'][] = 'flood_time'; 203 } 204 } 205 206 // perform more spam check 207 $comment_action = trigger_change('user_comment_check', 208 $comment_action, $comm 209 ); 210 211 if ( $comment_action!='reject' ) 212 { 213 $query = ' 214INSERT INTO '.COMMENTS_TABLE.' 215 (author, author_id, anonymous_id, content, date, validated, validation_date, image_id, website_url, email) 216 VALUES ( 217 \''.$comm['author'].'\', 218 '.$comm['author_id'].', 219 \''.$comm['ip'].'\', 220 \''.$comm['content'].'\', 221 NOW(), 222 \''.($comment_action=='validate' ? 'true':'false').'\', 223 '.($comment_action=='validate' ? 'NOW()':'NULL').', 224 '.$comm['image_id'].', 225 '.(!empty($comm['website_url']) ? '\''.$comm['website_url'].'\'' : 'NULL').', 226 '.(!empty($comm['email']) ? '\''.$comm['email'].'\'' : 'NULL').' 227 ) 228'; 229 pwg_query($query); 230 $comm['id'] = pwg_db_insert_id(COMMENTS_TABLE); 231 232 invalidate_user_cache_nb_comments(); 233 234 if ( ($conf['email_admin_on_comment'] && 'validate' == $comment_action) 235 or ($conf['email_admin_on_comment_validation'] and 'moderate' == $comment_action)) 236 { 237 include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php'); 238 239 $comment_url = get_absolute_root_url().'comments.php?comment_id='.$comm['id']; 240 241 $keyargs_content = array( 242 get_l10n_args('Author: %s', stripslashes($comm['author']) ), 243 get_l10n_args('Email: %s', stripslashes($comm['email']) ), 244 get_l10n_args('Comment: %s', stripslashes($comm['content']) ), 245 get_l10n_args(''), 246 get_l10n_args('Manage this user comment: %s', $comment_url), 247 ); 248 249 if ('moderate' == $comment_action) 250 { 251 $keyargs_content[] = get_l10n_args('(!) This comment requires validation'); 252 } 253 254 pwg_mail_notification_admins( 255 get_l10n_args('Comment by %s', stripslashes($comm['author']) ), 256 $keyargs_content 257 ); 258 } 259 } 260 261 return $comment_action; 262} 263 264/** 265 * Tries to delete a (or more) user comment. 266 * only admin can delete all comments 267 * other users can delete their own comments 268 * 269 * @param int|int[] $comment_id 270 * @return bool false if nothing deleted 271 */ 272function delete_user_comment($comment_id) 273{ 274 $user_where_clause = ''; 275 if (!is_admin()) 276 { 277 $user_where_clause = ' AND author_id = \''.$GLOBALS['user']['id'].'\''; 278 } 279 280 if (is_array($comment_id)) 281 $where_clause = 'id IN('.implode(',', $comment_id).')'; 282 else 283 $where_clause = 'id = '.$comment_id; 284 285 $query = ' 286DELETE FROM '.COMMENTS_TABLE.' 287 WHERE '.$where_clause. 288$user_where_clause.' 289;'; 290 291 if ( pwg_db_changes(pwg_query($query)) ) 292 { 293 invalidate_user_cache_nb_comments(); 294 295 email_admin('delete', 296 array('author' => $GLOBALS['user']['username'], 297 'comment_id' => $comment_id 298 )); 299 trigger_notify('user_comment_deletion', $comment_id); 300 301 return true; 302 } 303 304 return false; 305} 306 307/** 308 * Tries to update a user comment 309 * only admin can update all comments 310 * users can edit their own comments if admin allow them 311 * 312 * @param array $comment 313 * @param string $post_key secret key sent back to the browser 314 * @return string validate, moderate, reject 315 */ 316 317function update_user_comment($comment, $post_key) 318{ 319 global $conf, $page; 320 321 $comment_action = 'validate'; 322 323 if ( !verify_ephemeral_key($post_key, $comment['image_id']) ) 324 { 325 $comment_action='reject'; 326 } 327 elseif (!$conf['comments_validation'] or is_admin()) // should the updated comment must be validated 328 { 329 $comment_action='validate'; //one of validate, moderate, reject 330 } 331 else 332 { 333 $comment_action='moderate'; //one of validate, moderate, reject 334 } 335 336 // perform more spam check 337 $comment_action = 338 trigger_change('user_comment_check', 339 $comment_action, 340 array_merge($comment, 341 array('author' => $GLOBALS['user']['username']) 342 ) 343 ); 344 345 // website 346 if (!empty($comment['website_url'])) 347 { 348 $comm['website_url'] = strip_tags($comm['website_url']); 349 if (!preg_match('/^https?/i', $comment['website_url'])) 350 { 351 $comment['website_url'] = 'http://'.$comment['website_url']; 352 } 353 if (!url_check_format($comment['website_url'])) 354 { 355 $page['errors'][] = l10n('Your website URL is invalid'); 356 $comment_action='reject'; 357 } 358 } 359 360 if ( $comment_action!='reject' ) 361 { 362 $user_where_clause = ''; 363 if (!is_admin()) 364 { 365 $user_where_clause = ' AND author_id = \''. 366 $GLOBALS['user']['id'].'\''; 367 } 368 369 $query = ' 370UPDATE '.COMMENTS_TABLE.' 371 SET content = \''.$comment['content'].'\', 372 website_url = '.(!empty($comment['website_url']) ? '\''.$comment['website_url'].'\'' : 'NULL').', 373 validated = \''.($comment_action=='validate' ? 'true':'false').'\', 374 validation_date = '.($comment_action=='validate' ? 'NOW()':'NULL').' 375 WHERE id = '.$comment['comment_id']. 376$user_where_clause.' 377;'; 378 $result = pwg_query($query); 379 380 // mail admin and ask to validate the comment 381 if ($result and $conf['email_admin_on_comment_validation'] and 'moderate' == $comment_action) 382 { 383 include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php'); 384 385 $comment_url = get_absolute_root_url().'comments.php?comment_id='.$comment['comment_id']; 386 387 $keyargs_content = array( 388 get_l10n_args('Author: %s', stripslashes($GLOBALS['user']['username']) ), 389 get_l10n_args('Comment: %s', stripslashes($comment['content']) ), 390 get_l10n_args(''), 391 get_l10n_args('Manage this user comment: %s', $comment_url), 392 get_l10n_args('(!) This comment requires validation'), 393 ); 394 395 pwg_mail_notification_admins( 396 get_l10n_args('Comment by %s', stripslashes($GLOBALS['user']['username']) ), 397 $keyargs_content 398 ); 399 } 400 // just mail admin 401 elseif ($result) 402 { 403 email_admin('edit', array('author' => $GLOBALS['user']['username'], 404 'content' => stripslashes($comment['content'])) ); 405 } 406 } 407 408 return $comment_action; 409} 410 411/** 412 * Notifies admins about updated or deleted comment. 413 * Only used when no validation is needed, otherwise pwg_mail_notification_admins() is used. 414 * 415 * @param string $action edit, delete 416 * @param array $comment 417 */ 418function email_admin($action, $comment) 419{ 420 global $conf; 421 422 if (!in_array($action, array('edit', 'delete')) 423 or (($action=='edit') and !$conf['email_admin_on_comment_edition']) 424 or (($action=='delete') and !$conf['email_admin_on_comment_deletion'])) 425 { 426 return; 427 } 428 429 include_once(PHPWG_ROOT_PATH.'include/functions_mail.inc.php'); 430 431 $keyargs_content = array( 432 get_l10n_args('Author: %s', $comment['author']), 433 ); 434 435 if ($action=='delete') 436 { 437 $keyargs_content[] = get_l10n_args('This author removed the comment with id %d', $comment['comment_id']); 438 } 439 else 440 { 441 $keyargs_content[] = get_l10n_args('This author modified following comment:'); 442 $keyargs_content[] = get_l10n_args('Comment: %s', $comment['content']); 443 } 444 445 pwg_mail_notification_admins( 446 get_l10n_args('Comment by %s', $comment['author']), 447 $keyargs_content 448 ); 449} 450 451/** 452 * Returns the author id of a comment 453 * 454 * @param int $comment_id 455 * @param bool $die_on_error 456 * @return int 457 */ 458function get_comment_author_id($comment_id, $die_on_error=true) 459{ 460 $query = ' 461SELECT 462 author_id 463 FROM '.COMMENTS_TABLE.' 464 WHERE id = '.$comment_id.' 465;'; 466 $result = pwg_query($query); 467 if (pwg_db_num_rows($result) == 0) 468 { 469 if ($die_on_error) 470 { 471 fatal_error('Unknown comment identifier'); 472 } 473 else 474 { 475 return false; 476 } 477 } 478 479 list($author_id) = pwg_db_fetch_row($result); 480 481 return $author_id; 482} 483 484/** 485 * Tries to validate a user comment. 486 * 487 * @param int|int[] $comment_id 488 */ 489function validate_user_comment($comment_id) 490{ 491 if (is_array($comment_id)) 492 $where_clause = 'id IN('.implode(',', $comment_id).')'; 493 else 494 $where_clause = 'id = '.$comment_id; 495 496 $query = ' 497UPDATE '.COMMENTS_TABLE.' 498 SET validated = \'true\' 499 , validation_date = NOW() 500 WHERE '.$where_clause.' 501;'; 502 pwg_query($query); 503 504 invalidate_user_cache_nb_comments(); 505 trigger_notify('user_comment_validation', $comment_id); 506} 507 508/** 509 * Clears cache of nb comments for all users 510 */ 511function invalidate_user_cache_nb_comments() 512{ 513 global $user; 514 515 unset($user['nb_available_comments']); 516 517 $query = ' 518UPDATE '.USER_CACHE_TABLE.' 519 SET nb_available_comments = NULL 520;'; 521 pwg_query($query); 522} 523 524?>