[ Index ] |
PHP Cross Reference of MyBB 1.8.38 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * MyBB 1.8 4 * Copyright 2014 MyBB Group, All Rights Reserved 5 * 6 * Website: http://www.mybb.com 7 * License: http://www.mybb.com/about/license 8 * 9 */ 10 11 // Disallow direct access to this file for security reasons 12 if(!defined("IN_MYBB")) 13 { 14 die("Direct initialization of this file is not allowed.<br /><br />Please make sure IN_MYBB is defined."); 15 } 16 17 /** 18 * PM handling class, provides common structure to handle private messaging data. 19 * 20 */ 21 class PMDataHandler extends DataHandler 22 { 23 /** 24 * The language file used in the data handler. 25 * 26 * @var string 27 */ 28 public $language_file = 'datahandler_pm'; 29 30 /** 31 * The prefix for the language variables used in the data handler. 32 * 33 * @var string 34 */ 35 public $language_prefix = 'pmdata'; 36 37 /** 38 * Array of data inserted in to a private message. 39 * 40 * @var array 41 */ 42 public $pm_insert_data = array(); 43 44 /** 45 * Array of data used to update a private message. 46 * 47 * @var array 48 */ 49 public $pm_update_data = array(); 50 51 /** 52 * PM ID currently being manipulated by the datahandlers. 53 * 54 * @var int 55 */ 56 public $pmid = 0; 57 58 /** 59 * Values to be returned after inserting a PM. 60 * 61 * @var array 62 */ 63 public $return_values = array(); 64 65 /** 66 * Verifies a private message subject. 67 * 68 * @return boolean True when valid, false when invalid. 69 */ 70 function verify_subject() 71 { 72 $subject = &$this->data['subject']; 73 74 // Subject is over 85 characters, too long. 75 if(my_strlen($subject) > 85) 76 { 77 $this->set_error("too_long_subject"); 78 return false; 79 } 80 // No subject, apply the default [no subject] 81 if(!trim_blank_chrs($subject)) 82 { 83 $this->set_error("missing_subject"); 84 return false; 85 } 86 return true; 87 } 88 89 /** 90 * Verifies if a message for a PM is valid. 91 * 92 * @return boolean True when valid, false when invalid. 93 */ 94 function verify_message() 95 { 96 $message = &$this->data['message']; 97 98 // No message, return an error. 99 if(trim_blank_chrs($message) == '') 100 { 101 $this->set_error("missing_message"); 102 return false; 103 } 104 105 // If the length of message is beyond SQL limitation for 'text' field 106 else if(strlen($message) > 65535) 107 { 108 $this->set_error("message_too_long", array('65535', strlen($message))); 109 return false; 110 } 111 112 return true; 113 } 114 115 /** 116 * Verifies if the specified sender is valid or not. 117 * 118 * @return boolean True when valid, false when invalid. 119 */ 120 function verify_sender() 121 { 122 global $db, $mybb, $lang; 123 124 $pm = &$this->data; 125 126 // Return if we've already validated 127 if(!empty($pm['sender'])) 128 { 129 return true; 130 } 131 132 if($pm['fromid'] <= 0) 133 { 134 $pm['sender'] = array( 135 "uid" => 0, 136 "username" => '' 137 ); 138 139 return true; 140 } 141 142 // Fetch the senders profile data. 143 $sender = get_user($pm['fromid']); 144 145 // Collect user permissions for the sender. 146 $sender_permissions = user_permissions($pm['fromid']); 147 148 // Check if the sender is over their quota or not - if they are, disable draft sending 149 if(isset($pm['options']['savecopy']) && $pm['options']['savecopy'] != 0 && empty($pm['saveasdraft'])) 150 { 151 if($sender_permissions['pmquota'] != 0 && $sender['totalpms'] >= $sender_permissions['pmquota'] && $this->admin_override != true) 152 { 153 $pm['options']['savecopy'] = 0; 154 } 155 } 156 157 // Assign the sender information to the data. 158 $pm['sender'] = array( 159 "uid" => $sender['uid'], 160 "username" => $sender['username'] 161 ); 162 163 return true; 164 } 165 166 /** 167 * Verifies if an array of recipients for a private message are valid 168 * 169 * @return boolean True when valid, false when invalid. 170 */ 171 function verify_recipient() 172 { 173 global $cache, $db, $mybb, $lang; 174 175 $pm = &$this->data; 176 177 $recipients = array(); 178 179 $invalid_recipients = array(); 180 // We have our recipient usernames but need to fetch user IDs 181 if(array_key_exists("to", $pm)) 182 { 183 foreach(array("to", "bcc") as $recipient_type) 184 { 185 if(!isset($pm[$recipient_type])) 186 { 187 $pm[$recipient_type] = array(); 188 } 189 if(!is_array($pm[$recipient_type])) 190 { 191 $pm[$recipient_type] = array($pm[$recipient_type]); 192 } 193 194 $pm[$recipient_type] = array_map('trim', $pm[$recipient_type]); 195 $pm[$recipient_type] = array_filter($pm[$recipient_type]); 196 197 // No recipients? Skip query 198 if(empty($pm[$recipient_type])) 199 { 200 if($recipient_type == 'to' && empty($pm['saveasdraft'])) 201 { 202 $this->set_error("no_recipients"); 203 return false; 204 } 205 continue; 206 } 207 208 $recipientUsernames = array_map(array($db, 'escape_string'), $pm[$recipient_type]); 209 $recipientUsernames = "'".implode("','", $recipientUsernames)."'"; 210 211 $query = $db->simple_select('users', '*', 'username IN('.$recipientUsernames.')'); 212 213 $validUsernames = array(); 214 215 while($user = $db->fetch_array($query)) 216 { 217 if($recipient_type == "bcc") 218 { 219 $user['bcc'] = 1; 220 } 221 222 $recipients[] = $user; 223 $validUsernames[] = $user['username']; 224 } 225 226 foreach($pm[$recipient_type] as $username) 227 { 228 if(!in_array($username, $validUsernames)) 229 { 230 $invalid_recipients[] = $username; 231 } 232 } 233 } 234 } 235 // We have recipient IDs 236 else 237 { 238 foreach(array("toid", "bccid") as $recipient_type) 239 { 240 if(!isset($pm[$recipient_type])) 241 { 242 $pm[$recipient_type] = array(); 243 } 244 if(!is_array($pm[$recipient_type])) 245 { 246 $pm[$recipient_type] = array($pm[$recipient_type]); 247 } 248 $pm[$recipient_type] = array_map('intval', $pm[$recipient_type]); 249 $pm[$recipient_type] = array_filter($pm[$recipient_type]); 250 251 // No recipients? Skip query 252 if(empty($pm[$recipient_type])) 253 { 254 if($recipient_type == 'toid' && !$pm['saveasdraft']) 255 { 256 $this->set_error("no_recipients"); 257 return false; 258 } 259 continue; 260 } 261 262 $recipientUids = "'".implode("','", $pm[$recipient_type])."'"; 263 264 $query = $db->simple_select('users', '*', 'uid IN('.$recipientUids.')'); 265 266 $validUids = array(); 267 268 while($user = $db->fetch_array($query)) 269 { 270 if($recipient_type == "bccid") 271 { 272 $user['bcc'] = 1; 273 } 274 275 $recipients[] = $user; 276 $validUids[] = $user['uid']; 277 } 278 279 foreach($pm[$recipient_type] as $uid) 280 { 281 if(!in_array($uid, $validUids)) 282 { 283 $invalid_recipients[] = $uid; 284 } 285 } 286 } 287 } 288 289 // If we have one or more invalid recipients and we're not saving a draft, error 290 if(count($invalid_recipients) > 0) 291 { 292 $invalid_recipients = implode($lang->comma, array_map("htmlspecialchars_uni", $invalid_recipients)); 293 $this->set_error("invalid_recipients", array($invalid_recipients)); 294 return false; 295 } 296 297 if($pm['fromid'] > 0) 298 { 299 $sender_permissions = user_permissions($pm['fromid']); 300 301 // Are we trying to send this message to more users than the permissions allow? 302 if($sender_permissions['maxpmrecipients'] > 0 && count($recipients) > $sender_permissions['maxpmrecipients'] && $this->admin_override != true) 303 { 304 $this->set_error("too_many_recipients", array($sender_permissions['maxpmrecipients'])); 305 } 306 } 307 308 // Now we're done with that we loop through each recipient 309 $pm['recipients'] = array(); 310 foreach($recipients as $user) 311 { 312 // Collect group permissions for this recipient. 313 $recipient_permissions = user_permissions($user['uid']); 314 315 // See if the sender is on the recipients ignore list and that either 316 // - admin_override is set or 317 // - sender is an administrator 318 if($this->admin_override != true && empty($sender_permissions['canoverridepm'])) 319 { 320 if(!empty($user['ignorelist']) && strpos(','.$user['ignorelist'].',', ','.$pm['fromid'].',') !== false) 321 { 322 $this->set_error("recipient_is_ignoring", array(htmlspecialchars_uni($user['username']))); 323 } 324 325 // Is the recipient only allowing private messages from their buddy list? 326 if(empty($pm['saveasdraft']) && $mybb->settings['allowbuddyonly'] == 1 && $user['receivefrombuddy'] == 1 && !empty($user['buddylist']) && strpos(','.$user['buddylist'].',', ','.$pm['fromid'].',') === false) 327 { 328 $this->set_error('recipient_has_buddy_only', array(htmlspecialchars_uni($user['username']))); 329 } 330 331 // Can the recipient actually receive private messages based on their permissions or user setting? 332 if(($user['receivepms'] == 0 || $recipient_permissions['canusepms'] == 0) && empty($pm['saveasdraft'])) 333 { 334 $this->set_error("recipient_pms_disabled", array(htmlspecialchars_uni($user['username']))); 335 return false; 336 } 337 } 338 339 // Check to see if the user has reached their private message quota - if they have, email them. 340 if($recipient_permissions['pmquota'] != 0 && $user['totalpms'] >= $recipient_permissions['pmquota'] && empty($sender_permissions['cancp']) && empty($pm['saveasdraft']) && !$this->admin_override) 341 { 342 if(trim($user['language']) != '' && $lang->language_exists($user['language'])) 343 { 344 $uselang = trim($user['language']); 345 } 346 elseif($mybb->settings['bblanguage']) 347 { 348 $uselang = $mybb->settings['bblanguage']; 349 } 350 else 351 { 352 $uselang = "english"; 353 } 354 if($uselang == $mybb->settings['bblanguage'] || !$uselang) 355 { 356 $emailsubject = $lang->emailsubject_reachedpmquota; 357 $emailmessage = $lang->email_reachedpmquota; 358 } 359 else 360 { 361 $userlang = new MyLanguage; 362 $userlang->set_path(MYBB_ROOT."inc/languages"); 363 $userlang->set_language($uselang); 364 $userlang->load("messages"); 365 $emailsubject = $userlang->emailsubject_reachedpmquota; 366 $emailmessage = $userlang->email_reachedpmquota; 367 } 368 $emailmessage = $lang->sprintf($emailmessage, $user['username'], $mybb->settings['bbname'], $mybb->settings['bburl']); 369 $emailsubject = $lang->sprintf($emailsubject, $mybb->settings['bbname'], $pm['subject']); 370 371 $new_email = array( 372 "mailto" => $db->escape_string($user['email']), 373 "mailfrom" => '', 374 "subject" => $db->escape_string($emailsubject), 375 "message" => $db->escape_string($emailmessage), 376 "headers" => '' 377 ); 378 379 $db->insert_query("mailqueue", $new_email); 380 $cache->update_mailqueue(); 381 382 if($this->admin_override != true) 383 { 384 $this->set_error("recipient_reached_quota", array(htmlspecialchars_uni($user['username']))); 385 } 386 } 387 388 // Everything looks good, assign some specifics about the recipient 389 $pm['recipients'][$user['uid']] = array( 390 "uid" => $user['uid'], 391 "username" => $user['username'], 392 "email" => $user['email'], 393 "lastactive" => $user['lastactive'], 394 "pmnotice" => $user['pmnotice'], 395 "pmnotify" => $user['pmnotify'], 396 "language" => $user['language'] 397 ); 398 399 // If this recipient is defined as a BCC recipient, save it 400 if(isset($user['bcc']) && $user['bcc'] == 1) 401 { 402 $pm['recipients'][$user['uid']]['bcc'] = 1; 403 } 404 } 405 return true; 406 } 407 408 /** 409 * Verify that the user is not flooding the system. 410 * 411 * @return boolean 412 */ 413 function verify_pm_flooding() 414 { 415 global $mybb, $db; 416 417 $pm = &$this->data; 418 419 // Check if post flooding is enabled within MyBB or if the admin override option is specified. 420 if($mybb->settings['pmfloodsecs'] > 0 && $pm['fromid'] > 0 && $this->admin_override == false && !is_moderator(0, '', $pm['fromid'])) 421 { 422 // Fetch the senders profile data. 423 $sender = get_user($pm['fromid']); 424 425 // Calculate last post 426 $query = $db->simple_select("privatemessages", "dateline", "fromid='".$db->escape_string($pm['fromid'])."' AND toid != '0'", array('order_by' => 'dateline', 'order_dir' => 'desc', 'limit' => 1)); 427 $sender['lastpm'] = $db->fetch_field($query, "dateline"); 428 429 // A little bit of calculation magic and moderator status checking. 430 if(TIME_NOW-$sender['lastpm'] <= $mybb->settings['pmfloodsecs']) 431 { 432 // Oops, user has been flooding - throw back error message. 433 $time_to_wait = ($mybb->settings['pmfloodsecs'] - (TIME_NOW-$sender['lastpm'])) + 1; 434 if($time_to_wait == 1) 435 { 436 $this->set_error("pm_flooding_one_second"); 437 } 438 else 439 { 440 $this->set_error("pm_flooding", array($time_to_wait)); 441 } 442 return false; 443 } 444 } 445 // All is well that ends well - return true. 446 return true; 447 } 448 449 /** 450 * Verifies if the various 'options' for sending PMs are valid. 451 * 452 * @return boolean True when valid, false when invalid. 453 */ 454 function verify_options() 455 { 456 $options = &$this->data['options']; 457 458 $this->verify_yesno_option($options, 'signature', 1); 459 $this->verify_yesno_option($options, 'savecopy', 1); 460 $this->verify_yesno_option($options, 'disablesmilies', 0); 461 462 // Requesting a read receipt? 463 if(isset($options['readreceipt']) && $options['readreceipt'] == 1) 464 { 465 $options['readreceipt'] = 1; 466 } 467 else 468 { 469 $options['readreceipt'] = 0; 470 } 471 return true; 472 } 473 474 /** 475 * Validate an entire private message. 476 * 477 * @return boolean True when valid, false when invalid. 478 */ 479 function validate_pm() 480 { 481 global $plugins; 482 483 $pm = &$this->data; 484 485 if(empty($pm['savedraft'])) 486 { 487 $this->verify_pm_flooding(); 488 } 489 490 // Verify all PM assets. 491 $this->verify_subject(); 492 493 $this->verify_sender(); 494 495 $this->verify_recipient(); 496 497 $this->verify_message(); 498 499 $this->verify_options(); 500 501 $plugins->run_hooks("datahandler_pm_validate", $this); 502 503 // Choose the appropriate folder to save in. 504 if(!empty($pm['saveasdraft'])) 505 { 506 $pm['folder'] = 3; 507 } 508 else 509 { 510 $pm['folder'] = 1; 511 } 512 513 // We are done validating, return. 514 $this->set_validated(true); 515 if(count($this->get_errors()) > 0) 516 { 517 return false; 518 } 519 else 520 { 521 return true; 522 } 523 } 524 525 /** 526 * Insert a new private message. 527 * 528 * @return array Array of PM useful data. 529 */ 530 function insert_pm() 531 { 532 global $cache, $db, $mybb, $plugins, $lang; 533 534 // Yes, validating is required. 535 if(!$this->get_validated()) 536 { 537 die("The PM needs to be validated before inserting it into the DB."); 538 } 539 if(count($this->get_errors()) > 0) 540 { 541 die("The PM is not valid."); 542 } 543 544 // Assign data to common variable 545 $pm = &$this->data; 546 547 if(empty($pm['pmid'])) 548 { 549 $pm['pmid'] = 0; 550 } 551 $pm['pmid'] = (int)$pm['pmid']; 552 553 if(empty($pm['icon']) || $pm['icon'] < 0) 554 { 555 $pm['icon'] = 0; 556 } 557 558 $uid = 0; 559 560 // Build recipient list 561 $recipient_list = array(); 562 if(isset($pm['recipients']) && is_array($pm['recipients'])) 563 { 564 foreach($pm['recipients'] as $recipient) 565 { 566 if(!empty($recipient['bcc'])) 567 { 568 $recipient_list['bcc'][] = $recipient['uid']; 569 } 570 else 571 { 572 $recipient_list['to'][] = $recipient['uid']; 573 $uid = $recipient['uid']; 574 } 575 } 576 } 577 578 $this->pm_insert_data = array( 579 'fromid' => (int)$pm['sender']['uid'], 580 'folder' => $pm['folder'], 581 'subject' => $db->escape_string($pm['subject']), 582 'icon' => (int)$pm['icon'], 583 'message' => $db->escape_string($pm['message']), 584 'dateline' => TIME_NOW, 585 'status' => 0, 586 'includesig' => $pm['options']['signature'], 587 'smilieoff' => $pm['options']['disablesmilies'], 588 'receipt' => (int)$pm['options']['readreceipt'], 589 'readtime' => 0, 590 'recipients' => $db->escape_string(my_serialize($recipient_list)), 591 'ipaddress' => $db->escape_binary($pm['ipaddress']) 592 ); 593 594 // Check if we're updating a draft or not. 595 $query = $db->simple_select("privatemessages", "pmid, deletetime", "folder='3' AND uid='".(int)$pm['sender']['uid']."' AND pmid='{$pm['pmid']}'"); 596 $draftcheck = $db->fetch_array($query); 597 598 // This PM was previously a draft 599 if($draftcheck) 600 { 601 if($draftcheck['deletetime']) 602 { 603 // This draft was a reply to a PM 604 $pm['pmid'] = $draftcheck['deletetime']; 605 $pm['do'] = "reply"; 606 } 607 608 // Delete the old draft as we no longer need it 609 $db->delete_query("privatemessages", "pmid='{$draftcheck['pmid']}'"); 610 } 611 612 // Saving this message as a draft 613 if(!empty($pm['saveasdraft'])) 614 { 615 $this->pm_insert_data['uid'] = $pm['sender']['uid']; 616 617 // If this is a reply, then piggyback into the deletetime to let us know in the future 618 if($pm['do'] == "reply" || $pm['do'] == "replyall") 619 { 620 $this->pm_insert_data['deletetime'] = $pm['pmid']; 621 } 622 623 $plugins->run_hooks("datahandler_pm_insert_updatedraft", $this); 624 625 $this->pmid = $db->insert_query("privatemessages", $this->pm_insert_data); 626 627 $plugins->run_hooks("datahandler_pm_insert_updatedraft_commit", $this); 628 629 // If this is a draft, end it here - below deals with complete messages 630 return array( 631 "draftsaved" => 1 632 ); 633 } 634 635 $this->pmid = array(); 636 637 // Save a copy of the PM for each of our recipients 638 foreach($pm['recipients'] as $recipient) 639 { 640 // Send email notification of new PM if it is enabled for the recipient 641 $query = $db->simple_select("privatemessages", "dateline", "uid='".$recipient['uid']."' AND folder='1'", array('order_by' => 'dateline', 'order_dir' => 'desc', 'limit' => 1)); 642 $lastpm = $db->fetch_array($query); 643 if($recipient['pmnotify'] == 1 && (empty($lastpm['dateline']) || $recipient['lastactive'] > $lastpm['dateline'])) 644 { 645 if($recipient['language'] != "" && $lang->language_exists($recipient['language'])) 646 { 647 $uselang = $recipient['language']; 648 } 649 elseif($mybb->settings['bblanguage']) 650 { 651 $uselang = $mybb->settings['bblanguage']; 652 } 653 else 654 { 655 $uselang = "english"; 656 } 657 if($uselang == $mybb->settings['bblanguage'] && !empty($lang->emailsubject_newpm)) 658 { 659 $emailsubject = $lang->emailsubject_newpm; 660 $emailmessage = $lang->email_newpm; 661 } 662 else 663 { 664 $userlang = new MyLanguage; 665 $userlang->set_path(MYBB_ROOT."inc/languages"); 666 $userlang->set_language($uselang); 667 $userlang->load("messages"); 668 $emailsubject = $userlang->emailsubject_newpm; 669 $emailmessage = $userlang->email_newpm; 670 } 671 672 if(!$pm['sender']['username']) 673 { 674 $pm['sender']['username'] = $lang->mybb_engine; 675 } 676 677 require_once MYBB_ROOT.'inc/class_parser.php'; 678 $parser = new Postparser; 679 680 $parser_options = array( 681 'me_username' => $pm['sender']['username'], 682 'filter_badwords' => 1 683 ); 684 685 $pm['message'] = $parser->text_parse_message($pm['message'], $parser_options); 686 687 $emailmessage = $lang->sprintf($emailmessage, $recipient['username'], $pm['sender']['username'], $mybb->settings['bbname'], $mybb->settings['bburl'], $pm['message']); 688 $emailsubject = $lang->sprintf($emailsubject, $mybb->settings['bbname'], $pm['subject']); 689 690 $new_email = array( 691 "mailto" => $db->escape_string($recipient['email']), 692 "mailfrom" => '', 693 "subject" => $db->escape_string($emailsubject), 694 "message" => $db->escape_string($emailmessage), 695 "headers" => '' 696 ); 697 698 $db->insert_query("mailqueue", $new_email); 699 $cache->update_mailqueue(); 700 } 701 702 $this->pm_insert_data['uid'] = $recipient['uid']; 703 $this->pm_insert_data['toid'] = $recipient['uid']; 704 705 $plugins->run_hooks("datahandler_pm_insert", $this); 706 707 $this->pmid[] = $db->insert_query("privatemessages", $this->pm_insert_data); 708 709 $plugins->run_hooks("datahandler_pm_insert_commit", $this); 710 711 // If PM noices/alerts are on, show! 712 if($recipient['pmnotice'] == 1) 713 { 714 $updated_user = array( 715 "pmnotice" => 2 716 ); 717 $db->update_query("users", $updated_user, "uid='{$recipient['uid']}'"); 718 } 719 720 // Update private message count (total, new and unread) for recipient 721 require_once MYBB_ROOT."/inc/functions_user.php"; 722 update_pm_count($recipient['uid'], 7, $recipient['lastactive']); 723 } 724 725 // Are we replying or forwarding an existing PM? 726 if($pm['pmid']) 727 { 728 if($pm['do'] == "reply" || $pm['do'] == "replyall") 729 { 730 $sql_array = array( 731 'status' => 3, 732 'statustime' => TIME_NOW 733 ); 734 $db->update_query("privatemessages", $sql_array, "pmid={$pm['pmid']} AND uid={$pm['sender']['uid']}"); 735 } 736 elseif($pm['do'] == "forward") 737 { 738 $sql_array = array( 739 'status' => 4, 740 'statustime' => TIME_NOW 741 ); 742 $db->update_query("privatemessages", $sql_array, "pmid={$pm['pmid']} AND uid={$pm['sender']['uid']}"); 743 } 744 } 745 746 // If we're saving a copy 747 if($pm['options']['savecopy'] != 0) 748 { 749 if(isset($recipient_list['to']) && is_array($recipient_list['to']) && count($recipient_list['to']) == 1) 750 { 751 $this->pm_insert_data['toid'] = $uid; 752 } 753 else 754 { 755 $this->pm_insert_data['toid'] = 0; 756 } 757 $this->pm_insert_data['uid'] = (int)$pm['sender']['uid']; 758 $this->pm_insert_data['folder'] = 2; 759 $this->pm_insert_data['status'] = 1; 760 $this->pm_insert_data['receipt'] = 0; 761 762 $plugins->run_hooks("datahandler_pm_insert_savedcopy", $this); 763 764 $db->insert_query("privatemessages", $this->pm_insert_data); 765 766 $plugins->run_hooks("datahandler_pm_insert_savedcopy_commit", $this); 767 768 // Because the sender saved a copy, update their total pm count 769 require_once MYBB_ROOT."/inc/functions_user.php"; 770 update_pm_count($pm['sender']['uid'], 1); 771 } 772 773 // Return back with appropriate data 774 $this->return_values = array( 775 "messagesent" => 1, 776 "pmids" => $this->pmid 777 ); 778 779 $plugins->run_hooks("datahandler_pm_insert_end", $this); 780 781 return $this->return_values; 782 } 783 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
2005 - 2021 © MyBB.de | Alle Rechte vorbehalten! | Sponsor: netcup | Cross-referenced by PHPXref |