Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_privmsgs.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 64.94 KiB


0001  <?php
0002  /**
0003  *
0004  * This file is part of the phpBB Forum Software package.
0005  *
0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007  * @license GNU General Public License, version 2 (GPL-2.0)
0008  *
0009  * For full copyright and license information, please see
0010  * the docs/CREDITS.txt file.
0011  *
0012  */
0013   
0014  /**
0015  */
0016  if (!defined('IN_PHPBB'))
0017  {
0018      exit;
0019  }
0020   
0021  /*
0022      Ability to simply add own rules by doing three things:
0023          1) Add an appropriate constant
0024          2) Add a new check array to the global_privmsgs_rules variable and the condition array (if one is required)
0025          3) Implement the rule logic in the check_rule() function
0026          4) Add a new language variable to ucp.php
0027   
0028          The user is then able to select the new rule. It will be checked against and handled as specified.
0029          To add new actions (yes, checks can be added here too) to the rule management, the core code has to be modified.
0030  */
0031   
0032  define('RULE_IS_LIKE', 1);        // Is Like
0033  define('RULE_IS_NOT_LIKE', 2);    // Is Not Like
0034  define('RULE_IS', 3);            // Is
0035  define('RULE_IS_NOT', 4);        // Is Not
0036  define('RULE_BEGINS_WITH', 5);    // Begins with
0037  define('RULE_ENDS_WITH', 6);    // Ends with
0038  define('RULE_IS_FRIEND', 7);    // Is Friend
0039  define('RULE_IS_FOE', 8);        // Is Foe
0040  define('RULE_IS_USER', 9);        // Is User
0041  define('RULE_IS_GROUP', 10);    // Is In Usergroup
0042  define('RULE_ANSWERED', 11);    // Answered
0043  define('RULE_FORWARDED', 12);    // Forwarded
0044  define('RULE_TO_GROUP', 14);    // Usergroup
0045  define('RULE_TO_ME', 15);        // Me
0046   
0047  define('ACTION_PLACE_INTO_FOLDER', 1);
0048  define('ACTION_MARK_AS_READ', 2);
0049  define('ACTION_MARK_AS_IMPORTANT', 3);
0050  define('ACTION_DELETE_MESSAGE', 4);
0051   
0052  define('CHECK_SUBJECT', 1);
0053  define('CHECK_SENDER', 2);
0054  define('CHECK_MESSAGE', 3);
0055  define('CHECK_STATUS', 4);
0056  define('CHECK_TO', 5);
0057   
0058  /**
0059  * Global private message rules
0060  * These rules define what to do if a rule is hit
0061  */
0062  $global_privmsgs_rules = array(
0063      CHECK_SUBJECT    => array(
0064          RULE_IS_LIKE        => array('check0' => 'message_subject'),
0065          RULE_IS_NOT_LIKE    => array('check0' => 'message_subject'),
0066          RULE_IS                => array('check0' => 'message_subject'),
0067          RULE_IS_NOT            => array('check0' => 'message_subject'),
0068          RULE_BEGINS_WITH    => array('check0' => 'message_subject'),
0069          RULE_ENDS_WITH        => array('check0' => 'message_subject'),
0070      ),
0071   
0072      CHECK_SENDER    => array(
0073          RULE_IS_LIKE        => array('check0' => 'username'),
0074          RULE_IS_NOT_LIKE    => array('check0' => 'username'),
0075          RULE_IS                => array('check0' => 'username'),
0076          RULE_IS_NOT            => array('check0' => 'username'),
0077          RULE_BEGINS_WITH    => array('check0' => 'username'),
0078          RULE_ENDS_WITH        => array('check0' => 'username'),
0079          RULE_IS_FRIEND        => array('check0' => 'friend'),
0080          RULE_IS_FOE            => array('check0' => 'foe'),
0081          RULE_IS_USER        => array('check0' => 'author_id'),
0082          RULE_IS_GROUP        => array('check0' => 'author_in_group'),
0083      ),
0084   
0085      CHECK_MESSAGE    => array(
0086          RULE_IS_LIKE        => array('check0' => 'message_text'),
0087          RULE_IS_NOT_LIKE    => array('check0' => 'message_text'),
0088          RULE_IS                => array('check0' => 'message_text'),
0089          RULE_IS_NOT            => array('check0' => 'message_text'),
0090      ),
0091   
0092      CHECK_STATUS    => array(
0093          RULE_ANSWERED        => array('check0' => 'pm_replied'),
0094          RULE_FORWARDED        => array('check0' => 'pm_forwarded'),
0095      ),
0096   
0097      CHECK_TO        => array(
0098          RULE_TO_GROUP        => array('check0' => 'to', 'check1' => 'bcc', 'check2' => 'user_in_group'),
0099          RULE_TO_ME            => array('check0' => 'to', 'check1' => 'bcc'),
0100      )
0101  );
0102   
0103  /**
0104  * This is for defining which condition fields to show for which Rule
0105  */
0106  $global_rule_conditions = array(
0107      RULE_IS_LIKE        => 'text',
0108      RULE_IS_NOT_LIKE    => 'text',
0109      RULE_IS                => 'text',
0110      RULE_IS_NOT            => 'text',
0111      RULE_BEGINS_WITH    => 'text',
0112      RULE_ENDS_WITH        => 'text',
0113      RULE_IS_USER        => 'user',
0114      RULE_IS_GROUP        => 'group'
0115  );
0116   
0117  /**
0118  * Get all folder
0119  */
0120  function get_folder($user_id, $folder_id = false)
0121  {
0122      global $db, $user, $template;
0123      global $phpbb_root_path, $phpEx;
0124   
0125      $folder = array();
0126   
0127      // Get folder information
0128      $sql = 'SELECT folder_id, COUNT(msg_id) as num_messages, SUM(pm_unread) as num_unread
0129          FROM ' . PRIVMSGS_TO_TABLE . "
0130          WHERE user_id = $user_id
0131              AND folder_id <> " . PRIVMSGS_NO_BOX . '
0132          GROUP BY folder_id';
0133      $result = $db->sql_query($sql);
0134   
0135      $num_messages = $num_unread = array();
0136      while ($row = $db->sql_fetchrow($result))
0137      {
0138          $num_messages[(int) $row['folder_id']] = $row['num_messages'];
0139          $num_unread[(int) $row['folder_id']] = $row['num_unread'];
0140      }
0141      $db->sql_freeresult($result);
0142   
0143      // Make sure the default boxes are defined
0144      $available_folder = array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX);
0145   
0146      foreach ($available_folder as $default_folder)
0147      {
0148          if (!isset($num_messages[$default_folder]))
0149          {
0150              $num_messages[$default_folder] = 0;
0151          }
0152   
0153          if (!isset($num_unread[$default_folder]))
0154          {
0155              $num_unread[$default_folder] = 0;
0156          }
0157      }
0158   
0159      // Adjust unread status for outbox
0160      $num_unread[PRIVMSGS_OUTBOX] = $num_messages[PRIVMSGS_OUTBOX];
0161   
0162      $folder[PRIVMSGS_INBOX] = array(
0163          'folder_name'        => $user->lang['PM_INBOX'],
0164          'num_messages'        => $num_messages[PRIVMSGS_INBOX],
0165          'unread_messages'    => $num_unread[PRIVMSGS_INBOX]
0166      );
0167   
0168      // Custom Folder
0169      $sql = 'SELECT folder_id, folder_name, pm_count
0170          FROM ' . PRIVMSGS_FOLDER_TABLE . "
0171              WHERE user_id = $user_id";
0172      $result = $db->sql_query($sql);
0173   
0174      while ($row = $db->sql_fetchrow($result))
0175      {
0176          $folder[$row['folder_id']] = array(
0177              'folder_name'        => $row['folder_name'],
0178              'num_messages'        => $row['pm_count'],
0179              'unread_messages'    => ((isset($num_unread[$row['folder_id']])) ? $num_unread[$row['folder_id']] : 0)
0180          );
0181      }
0182      $db->sql_freeresult($result);
0183   
0184      $folder[PRIVMSGS_OUTBOX] = array(
0185          'folder_name'        => $user->lang['PM_OUTBOX'],
0186          'num_messages'        => $num_messages[PRIVMSGS_OUTBOX],
0187          'unread_messages'    => $num_unread[PRIVMSGS_OUTBOX]
0188      );
0189   
0190      $folder[PRIVMSGS_SENTBOX] = array(
0191          'folder_name'        => $user->lang['PM_SENTBOX'],
0192          'num_messages'        => $num_messages[PRIVMSGS_SENTBOX],
0193          'unread_messages'    => $num_unread[PRIVMSGS_SENTBOX]
0194      );
0195   
0196      // Define Folder Array for template designers (and for making custom folders usable by the template too)
0197      foreach ($folder as $f_id => $folder_ary)
0198      {
0199          $folder_id_name = ($f_id == PRIVMSGS_INBOX) ? 'inbox' : (($f_id == PRIVMSGS_OUTBOX) ? 'outbox' : 'sentbox');
0200   
0201          $template->assign_block_vars('folder', array(
0202              'FOLDER_ID'            => $f_id,
0203              'FOLDER_NAME'        => $folder_ary['folder_name'],
0204              'NUM_MESSAGES'        => $folder_ary['num_messages'],
0205              'UNREAD_MESSAGES'    => $folder_ary['unread_messages'],
0206   
0207              'U_FOLDER'            => ($f_id > 0) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=' . $f_id) : append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=' . $folder_id_name),
0208   
0209              'S_CUR_FOLDER'        => ($f_id === $folder_id) ? true : false,
0210              'S_UNREAD_MESSAGES'    => ($folder_ary['unread_messages']) ? true : false,
0211              'S_CUSTOM_FOLDER'    => ($f_id > 0) ? true : false)
0212          );
0213      }
0214   
0215      if ($folder_id !== false && $folder_id !== PRIVMSGS_HOLD_BOX && !isset($folder[$folder_id]))
0216      {
0217          trigger_error('UNKNOWN_FOLDER');
0218      }
0219   
0220      return $folder;
0221  }
0222   
0223  /**
0224  * Delete Messages From Sentbox
0225  * we are doing this here because this saves us a bunch of checks and queries
0226  */
0227  function clean_sentbox($num_sentbox_messages)
0228  {
0229      global $db, $user;
0230   
0231      // Check Message Limit
0232      if ($user->data['message_limit'] && $num_sentbox_messages > $user->data['message_limit'])
0233      {
0234          // Delete old messages
0235          $sql = 'SELECT t.msg_id
0236              FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p
0237              WHERE t.msg_id = p.msg_id
0238                  AND t.user_id = ' . $user->data['user_id'] . '
0239                  AND t.folder_id = ' . PRIVMSGS_SENTBOX . '
0240              ORDER BY p.message_time ASC';
0241          $result = $db->sql_query_limit($sql, ($num_sentbox_messages - $user->data['message_limit']));
0242   
0243          $delete_ids = array();
0244          while ($row = $db->sql_fetchrow($result))
0245          {
0246              $delete_ids[] = $row['msg_id'];
0247          }
0248          $db->sql_freeresult($result);
0249          delete_pm($user->data['user_id'], $delete_ids, PRIVMSGS_SENTBOX);
0250      }
0251  }
0252   
0253  /**
0254  * Check Rule against Message Information
0255  */
0256  function check_rule(&$rules, &$rule_row, &$message_row, $user_id)
0257  {
0258      if (!isset($rules[$rule_row['rule_check']][$rule_row['rule_connection']]))
0259      {
0260          return false;
0261      }
0262   
0263      $check_ary = $rules[$rule_row['rule_check']][$rule_row['rule_connection']];
0264   
0265      $result = false;
0266   
0267      $check0 = $message_row[$check_ary['check0']];
0268   
0269      switch ($rule_row['rule_connection'])
0270      {
0271          case RULE_IS_LIKE:
0272              $result = preg_match("/" . preg_quote($rule_row['rule_string'], '/') . '/i', $check0);
0273          break;
0274   
0275          case RULE_IS_NOT_LIKE:
0276              $result = !preg_match("/" . preg_quote($rule_row['rule_string'], '/') . '/i', $check0);
0277          break;
0278   
0279          case RULE_IS:
0280              $result = ($check0 == $rule_row['rule_string']);
0281          break;
0282   
0283          case RULE_IS_NOT:
0284              $result = ($check0 != $rule_row['rule_string']);
0285          break;
0286   
0287          case RULE_BEGINS_WITH:
0288              $result = preg_match("/^" . preg_quote($rule_row['rule_string'], '/') . '/i', $check0);
0289          break;
0290   
0291          case RULE_ENDS_WITH:
0292              $result = preg_match("/" . preg_quote($rule_row['rule_string'], '/') . '$/i', $check0);
0293          break;
0294   
0295          case RULE_IS_FRIEND:
0296          case RULE_IS_FOE:
0297          case RULE_ANSWERED:
0298          case RULE_FORWARDED:
0299              $result = ($check0 == 1);
0300          break;
0301   
0302          case RULE_IS_USER:
0303              $result = ($check0 == $rule_row['rule_user_id']);
0304          break;
0305   
0306          case RULE_IS_GROUP:
0307              $result = in_array($rule_row['rule_group_id'], $check0);
0308          break;
0309   
0310          case RULE_TO_GROUP:
0311              $result = (in_array('g_' . $message_row[$check_ary['check2']], $check0) || in_array('g_' . $message_row[$check_ary['check2']], $message_row[$check_ary['check1']]));
0312          break;
0313   
0314          case RULE_TO_ME:
0315              $result = (in_array('u_' . $user_id, $check0) || in_array('u_' . $user_id, $message_row[$check_ary['check1']]));
0316          break;
0317      }
0318   
0319      if (!$result)
0320      {
0321          return false;
0322      }
0323   
0324      switch ($rule_row['rule_action'])
0325      {
0326          case ACTION_PLACE_INTO_FOLDER:
0327              return array('action' => $rule_row['rule_action'], 'folder_id' => $rule_row['rule_folder_id']);
0328          break;
0329   
0330          case ACTION_MARK_AS_READ:
0331          case ACTION_MARK_AS_IMPORTANT:
0332              return array('action' => $rule_row['rule_action'], 'pm_unread' => $message_row['pm_unread'], 'pm_marked' => $message_row['pm_marked']);
0333          break;
0334   
0335          case ACTION_DELETE_MESSAGE:
0336              global $db;
0337   
0338              // Check for admins/mods - users are not allowed to remove those messages...
0339              // We do the check here to make sure the data we use is consistent
0340              $sql = 'SELECT user_id, user_type, user_permissions
0341                  FROM ' . USERS_TABLE . '
0342                  WHERE user_id = ' . (int) $message_row['author_id'];
0343              $result = $db->sql_query($sql);
0344              $userdata = $db->sql_fetchrow($result);
0345              $db->sql_freeresult($result);
0346   
0347              $auth2 = new \phpbb\auth\auth();
0348              $auth2->acl($userdata);
0349   
0350              if (!$auth2->acl_get('a_') && !$auth2->acl_get('m_') && !$auth2->acl_getf_global('m_'))
0351              {
0352                  return array('action' => $rule_row['rule_action'], 'pm_unread' => $message_row['pm_unread'], 'pm_marked' => $message_row['pm_marked']);
0353              }
0354   
0355              return false;
0356          break;
0357   
0358          default:
0359              return false;
0360      }
0361   
0362      return false;
0363  }
0364   
0365  /**
0366  * Update user PM count
0367  */
0368  function update_pm_counts()
0369  {
0370      global $user, $db;
0371   
0372      // Update unread count
0373      $sql = 'SELECT COUNT(msg_id) as num_messages
0374          FROM ' . PRIVMSGS_TO_TABLE . '
0375          WHERE pm_unread = 1
0376              AND folder_id <> ' . PRIVMSGS_OUTBOX . '
0377              AND user_id = ' . $user->data['user_id'];
0378      $result = $db->sql_query($sql);
0379      $user->data['user_unread_privmsg'] = (int) $db->sql_fetchfield('num_messages');
0380      $db->sql_freeresult($result);
0381   
0382      // Update new pm count
0383      $sql = 'SELECT COUNT(msg_id) as num_messages
0384          FROM ' . PRIVMSGS_TO_TABLE . '
0385          WHERE pm_new = 1
0386              AND folder_id IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ')
0387              AND user_id = ' . $user->data['user_id'];
0388      $result = $db->sql_query($sql);
0389      $user->data['user_new_privmsg'] = (int) $db->sql_fetchfield('num_messages');
0390      $db->sql_freeresult($result);
0391   
0392      $db->sql_query('UPDATE ' . USERS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array(
0393          'user_unread_privmsg'    => (int) $user->data['user_unread_privmsg'],
0394          'user_new_privmsg'        => (int) $user->data['user_new_privmsg'],
0395      )) . ' WHERE user_id = ' . $user->data['user_id']);
0396   
0397      // Ok, here we need to repair something, other boxes than privmsgs_no_box and privmsgs_hold_box should not carry the pm_new flag.
0398      if (!$user->data['user_new_privmsg'])
0399      {
0400          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
0401              SET pm_new = 0
0402              WHERE pm_new = 1
0403                  AND folder_id NOT IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ')
0404                  AND user_id = ' . $user->data['user_id'];
0405          $db->sql_query($sql);
0406      }
0407  }
0408   
0409  /**
0410  * Place new messages into appropriate folder
0411  */
0412  function place_pm_into_folder(&$global_privmsgs_rules, $release = false)
0413  {
0414      global $db, $user, $config;
0415   
0416      if (!$user->data['user_new_privmsg'])
0417      {
0418          return array('not_moved' => 0, 'removed' => 0);
0419      }
0420   
0421      $user_message_rules = (int) $user->data['user_message_rules'];
0422      $user_id = (int) $user->data['user_id'];
0423   
0424      $action_ary = $move_into_folder = array();
0425      $num_removed = 0;
0426   
0427      // Newly processing on-hold messages
0428      if ($release)
0429      {
0430          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
0431              SET folder_id = ' . PRIVMSGS_NO_BOX . '
0432              WHERE folder_id = ' . PRIVMSGS_HOLD_BOX . "
0433                  AND user_id = $user_id";
0434          $db->sql_query($sql);
0435      }
0436   
0437      // Get those messages not yet placed into any box
0438      $retrieve_sql = 'SELECT t.*, p.*, u.username, u.user_id, u.group_id
0439          FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . " u
0440          WHERE t.user_id = $user_id
0441              AND p.author_id = u.user_id
0442              AND t.folder_id = " . PRIVMSGS_NO_BOX . '
0443              AND t.msg_id = p.msg_id';
0444   
0445      // Just place into the appropriate arrays if no rules need to be checked
0446      if (!$user_message_rules)
0447      {
0448          $result = $db->sql_query($retrieve_sql);
0449   
0450          while ($row = $db->sql_fetchrow($result))
0451          {
0452              $action_ary[$row['msg_id']][] = array('action' => false);
0453          }
0454          $db->sql_freeresult($result);
0455      }
0456      else
0457      {
0458          $user_rules = $zebra = $check_rows = array();
0459          $user_ids = $memberships = array();
0460   
0461          // First of all, grab all rules and retrieve friends/foes
0462          $sql = 'SELECT *
0463              FROM ' . PRIVMSGS_RULES_TABLE . "
0464              WHERE user_id = $user_id";
0465          $result = $db->sql_query($sql);
0466          $user_rules = $db->sql_fetchrowset($result);
0467          $db->sql_freeresult($result);
0468   
0469          if (sizeof($user_rules))
0470          {
0471              $sql = 'SELECT zebra_id, friend, foe
0472                  FROM ' . ZEBRA_TABLE . "
0473                  WHERE user_id = $user_id";
0474              $result = $db->sql_query($sql);
0475   
0476              while ($row = $db->sql_fetchrow($result))
0477              {
0478                  $zebra[$row['zebra_id']] = $row;
0479              }
0480              $db->sql_freeresult($result);
0481          }
0482   
0483          // Now build a bare-bone check_row array
0484          $result = $db->sql_query($retrieve_sql);
0485   
0486          while ($row = $db->sql_fetchrow($result))
0487          {
0488              $check_rows[] = array_merge($row, array(
0489                  'to'                => explode(':', $row['to_address']),
0490                  'bcc'                => explode(':', $row['bcc_address']),
0491                  'friend'            => (isset($zebra[$row['author_id']])) ? $zebra[$row['author_id']]['friend'] : 0,
0492                  'foe'                => (isset($zebra[$row['author_id']])) ? $zebra[$row['author_id']]['foe'] : 0,
0493                  'user_in_group'        => array($user->data['group_id']),
0494                  'author_in_group'    => array())
0495              );
0496   
0497              $user_ids[] = $row['user_id'];
0498          }
0499          $db->sql_freeresult($result);
0500   
0501          // Retrieve user memberships
0502          if (sizeof($user_ids))
0503          {
0504              $sql = 'SELECT *
0505                  FROM ' . USER_GROUP_TABLE . '
0506                  WHERE ' . $db->sql_in_set('user_id', $user_ids) . '
0507                      AND user_pending = 0';
0508              $result = $db->sql_query($sql);
0509   
0510              while ($row = $db->sql_fetchrow($result))
0511              {
0512                  $memberships[$row['user_id']][] = $row['group_id'];
0513              }
0514              $db->sql_freeresult($result);
0515          }
0516   
0517          // Now place into the appropriate folder
0518          foreach ($check_rows as $row)
0519          {
0520              // Add membership if set
0521              if (isset($memberships[$row['author_id']]))
0522              {
0523                  $row['author_in_group'] = $memberships[$row['user_id']];
0524              }
0525   
0526              // Check Rule - this should be very quick since we have all information we need
0527              $is_match = false;
0528              foreach ($user_rules as $rule_row)
0529              {
0530                  if (($action = check_rule($global_privmsgs_rules, $rule_row, $row, $user_id)) !== false)
0531                  {
0532                      $is_match = true;
0533                      $action_ary[$row['msg_id']][] = $action;
0534                  }
0535              }
0536   
0537              if (!$is_match)
0538              {
0539                  $action_ary[$row['msg_id']][] = array('action' => false);
0540              }
0541          }
0542   
0543          unset($user_rules, $zebra, $check_rows, $user_ids, $memberships);
0544      }
0545   
0546      // We place actions into arrays, to save queries.
0547      $unread_ids = $delete_ids = $important_ids = array();
0548   
0549      foreach ($action_ary as $msg_id => $msg_ary)
0550      {
0551          // It is allowed to execute actions more than once, except placing messages into folder
0552          $folder_action = $message_removed = false;
0553   
0554          foreach ($msg_ary as $pos => $rule_ary)
0555          {
0556              if ($folder_action && $rule_ary['action'] == ACTION_PLACE_INTO_FOLDER)
0557              {
0558                  continue;
0559              }
0560   
0561              switch ($rule_ary['action'])
0562              {
0563                  case ACTION_PLACE_INTO_FOLDER:
0564                      // Folder actions have precedence, so we will remove any other ones
0565                      $folder_action = true;
0566                      $move_into_folder[(int) $rule_ary['folder_id']][] = $msg_id;
0567                  break;
0568   
0569                  case ACTION_MARK_AS_READ:
0570                      if ($rule_ary['pm_unread'])
0571                      {
0572                          $unread_ids[] = $msg_id;
0573                      }
0574                  break;
0575   
0576                  case ACTION_DELETE_MESSAGE:
0577                      $delete_ids[] = $msg_id;
0578                      $message_removed = true;
0579                  break;
0580   
0581                  case ACTION_MARK_AS_IMPORTANT:
0582                      if (!$rule_ary['pm_marked'])
0583                      {
0584                          $important_ids[] = $msg_id;
0585                      }
0586                  break;
0587              }
0588          }
0589   
0590          // We place this here because it could happen that the messages are doubled if a rule marks a message and then moves it into a specific
0591          // folder. Here we simply move the message into the INBOX if it gets not removed and also not put into a custom folder.
0592          if (!$folder_action && !$message_removed)
0593          {
0594              $move_into_folder[PRIVMSGS_INBOX][] = $msg_id;
0595          }
0596      }
0597   
0598      // Do not change the order of processing
0599      // The number of queries needed to be executed here highly depends on the defined rules and are
0600      // only gone through if new messages arrive.
0601   
0602      // Delete messages
0603      if (sizeof($delete_ids))
0604      {
0605          $num_removed += sizeof($delete_ids);
0606          delete_pm($user_id, $delete_ids, PRIVMSGS_NO_BOX);
0607      }
0608   
0609      // Set messages to Unread
0610      if (sizeof($unread_ids))
0611      {
0612          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
0613              SET pm_unread = 0
0614              WHERE ' . $db->sql_in_set('msg_id', $unread_ids) . "
0615                  AND user_id = $user_id
0616                  AND folder_id = " . PRIVMSGS_NO_BOX;
0617          $db->sql_query($sql);
0618      }
0619   
0620      // mark messages as important
0621      if (sizeof($important_ids))
0622      {
0623          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
0624              SET pm_marked = 1 - pm_marked
0625              WHERE folder_id = ' . PRIVMSGS_NO_BOX . "
0626                  AND user_id = $user_id
0627                  AND " . $db->sql_in_set('msg_id', $important_ids);
0628          $db->sql_query($sql);
0629      }
0630   
0631      // Move into folder
0632      $folder = array();
0633   
0634      if (sizeof($move_into_folder))
0635      {
0636          // Determine Full Folder Action - we need the move to folder id later eventually
0637          $full_folder_action = ($user->data['user_full_folder'] == FULL_FOLDER_NONE) ? ($config['full_folder_action'] - (FULL_FOLDER_NONE*(-1))) : $user->data['user_full_folder'];
0638   
0639          $sql_folder = array_keys($move_into_folder);
0640          if ($full_folder_action >= 0)
0641          {
0642              $sql_folder[] = $full_folder_action;
0643          }
0644   
0645          $sql = 'SELECT folder_id, pm_count
0646              FROM ' . PRIVMSGS_FOLDER_TABLE . '
0647              WHERE ' . $db->sql_in_set('folder_id', $sql_folder) . "
0648                  AND user_id = $user_id";
0649          $result = $db->sql_query($sql);
0650   
0651          while ($row = $db->sql_fetchrow($result))
0652          {
0653              $folder[(int) $row['folder_id']] = (int) $row['pm_count'];
0654          }
0655          $db->sql_freeresult($result);
0656   
0657          unset($sql_folder);
0658   
0659          if (isset($move_into_folder[PRIVMSGS_INBOX]))
0660          {
0661              $sql = 'SELECT COUNT(msg_id) as num_messages
0662                  FROM ' . PRIVMSGS_TO_TABLE . "
0663                  WHERE user_id = $user_id
0664                      AND folder_id = " . PRIVMSGS_INBOX;
0665              $result = $db->sql_query($sql);
0666              $folder[PRIVMSGS_INBOX] = (int) $db->sql_fetchfield('num_messages');
0667              $db->sql_freeresult($result);
0668          }
0669      }
0670   
0671      // Here we have ideally only one folder to move into
0672      foreach ($move_into_folder as $folder_id => $msg_ary)
0673      {
0674          $dest_folder = $folder_id;
0675          $full_folder_action = FULL_FOLDER_NONE;
0676   
0677          // Check Message Limit - we calculate with the complete array, most of the time it is one message
0678          // But we are making sure that the other way around works too (more messages in queue than allowed to be stored)
0679          if ($user->data['message_limit'] && $folder[$folder_id] && ($folder[$folder_id] + sizeof($msg_ary)) > $user->data['message_limit'])
0680          {
0681              $full_folder_action = ($user->data['user_full_folder'] == FULL_FOLDER_NONE) ? ($config['full_folder_action'] - (FULL_FOLDER_NONE*(-1))) : $user->data['user_full_folder'];
0682   
0683              // If destination folder itself is full...
0684              if ($full_folder_action >= 0 && ($folder[$full_folder_action] + sizeof($msg_ary)) > $user->data['message_limit'])
0685              {
0686                  $full_folder_action = $config['full_folder_action'] - (FULL_FOLDER_NONE*(-1));
0687              }
0688   
0689              // If Full Folder Action is to move to another folder, we simply adjust the destination folder
0690              if ($full_folder_action >= 0)
0691              {
0692                  $dest_folder = $full_folder_action;
0693              }
0694              else if ($full_folder_action == FULL_FOLDER_DELETE)
0695              {
0696                  // Delete some messages. NOTE: Ordered by msg_id here instead of message_time!
0697                  $sql = 'SELECT msg_id
0698                      FROM ' . PRIVMSGS_TO_TABLE . "
0699                      WHERE user_id = $user_id
0700                          AND folder_id = $dest_folder
0701                      ORDER BY msg_id ASC";
0702                  $result = $db->sql_query_limit($sql, (($folder[$dest_folder] + sizeof($msg_ary)) - $user->data['message_limit']));
0703   
0704                  $delete_ids = array();
0705                  while ($row = $db->sql_fetchrow($result))
0706                  {
0707                      $delete_ids[] = $row['msg_id'];
0708                  }
0709                  $db->sql_freeresult($result);
0710   
0711                  $num_removed += sizeof($delete_ids);
0712                  delete_pm($user_id, $delete_ids, $dest_folder);
0713              }
0714          }
0715   
0716          //
0717          if ($full_folder_action == FULL_FOLDER_HOLD)
0718          {
0719              $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
0720                  SET folder_id = ' . PRIVMSGS_HOLD_BOX . '
0721                  WHERE folder_id = ' . PRIVMSGS_NO_BOX . "
0722                      AND user_id = $user_id
0723                      AND " . $db->sql_in_set('msg_id', $msg_ary);
0724              $db->sql_query($sql);
0725          }
0726          else
0727          {
0728              $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
0729                  SET folder_id = $dest_folder, pm_new = 0
0730                  WHERE folder_id = " . PRIVMSGS_NO_BOX . "
0731                      AND user_id = $user_id
0732                      AND pm_new = 1
0733                      AND " . $db->sql_in_set('msg_id', $msg_ary);
0734              $db->sql_query($sql);
0735   
0736              if ($dest_folder != PRIVMSGS_INBOX)
0737              {
0738                  $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . '
0739                      SET pm_count = pm_count + ' . (int) $db->sql_affectedrows() . "
0740                      WHERE folder_id = $dest_folder
0741                          AND user_id = $user_id";
0742                  $db->sql_query($sql);
0743              }
0744          }
0745      }
0746   
0747      if (sizeof($action_ary))
0748      {
0749          // Move from OUTBOX to SENTBOX
0750          // We are not checking any full folder status here... SENTBOX is a special treatment (old messages get deleted)
0751          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
0752              SET folder_id = ' . PRIVMSGS_SENTBOX . '
0753              WHERE folder_id = ' . PRIVMSGS_OUTBOX . '
0754                  AND ' . $db->sql_in_set('msg_id', array_keys($action_ary));
0755          $db->sql_query($sql);
0756      }
0757   
0758      // Update new/unread count
0759      update_pm_counts();
0760   
0761      // Now check how many messages got not moved...
0762      $sql = 'SELECT COUNT(msg_id) as num_messages
0763          FROM ' . PRIVMSGS_TO_TABLE . "
0764          WHERE user_id = $user_id
0765              AND folder_id = " . PRIVMSGS_HOLD_BOX;
0766      $result = $db->sql_query($sql);
0767      $num_not_moved = (int) $db->sql_fetchfield('num_messages');
0768      $db->sql_freeresult($result);
0769   
0770      return array('not_moved' => $num_not_moved, 'removed' => $num_removed);
0771  }
0772   
0773  /**
0774  * Move PM from one to another folder
0775  */
0776  function move_pm($user_id, $message_limit, $move_msg_ids, $dest_folder, $cur_folder_id)
0777  {
0778      global $db, $user;
0779      global $phpbb_root_path, $phpEx;
0780   
0781      $num_moved = 0;
0782   
0783      if (!is_array($move_msg_ids))
0784      {
0785          $move_msg_ids = array($move_msg_ids);
0786      }
0787   
0788      if (sizeof($move_msg_ids) && !in_array($dest_folder, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX)) &&
0789          !in_array($cur_folder_id, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX)) && $cur_folder_id != $dest_folder)
0790      {
0791          // We have to check the destination folder ;)
0792          if ($dest_folder != PRIVMSGS_INBOX)
0793          {
0794              $sql = 'SELECT folder_id, folder_name, pm_count
0795                  FROM ' . PRIVMSGS_FOLDER_TABLE . "
0796                  WHERE folder_id = $dest_folder
0797                      AND user_id = $user_id";
0798              $result = $db->sql_query($sql);
0799              $row = $db->sql_fetchrow($result);
0800              $db->sql_freeresult($result);
0801   
0802              if (!$row)
0803              {
0804                  send_status_line(403, 'Forbidden');
0805                  trigger_error('NOT_AUTHORISED');
0806              }
0807   
0808              if ($message_limit && $row['pm_count'] + sizeof($move_msg_ids) > $message_limit)
0809              {
0810                  $message = sprintf($user->lang['NOT_ENOUGH_SPACE_FOLDER'], $row['folder_name']) . '<br /><br />';
0811                  $message .= sprintf($user->lang['CLICK_RETURN_FOLDER'], '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=' . $row['folder_id']) . '">', '</a>', $row['folder_name']);
0812                  trigger_error($message);
0813              }
0814          }
0815          else
0816          {
0817              $sql = 'SELECT COUNT(msg_id) as num_messages
0818                  FROM ' . PRIVMSGS_TO_TABLE . '
0819                  WHERE folder_id = ' . PRIVMSGS_INBOX . "
0820                      AND user_id = $user_id";
0821              $result = $db->sql_query($sql);
0822              $num_messages = (int) $db->sql_fetchfield('num_messages');
0823              $db->sql_freeresult($result);
0824   
0825              if ($message_limit && $num_messages + sizeof($move_msg_ids) > $message_limit)
0826              {
0827                  $message = sprintf($user->lang['NOT_ENOUGH_SPACE_FOLDER'], $user->lang['PM_INBOX']) . '<br /><br />';
0828                  $message .= sprintf($user->lang['CLICK_RETURN_FOLDER'], '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox') . '">', '</a>', $user->lang['PM_INBOX']);
0829                  trigger_error($message);
0830              }
0831          }
0832   
0833          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
0834              SET folder_id = $dest_folder
0835              WHERE folder_id = $cur_folder_id
0836                  AND user_id = $user_id
0837                  AND " . $db->sql_in_set('msg_id', $move_msg_ids);
0838          $db->sql_query($sql);
0839          $num_moved = $db->sql_affectedrows();
0840   
0841          // Update pm counts
0842          if ($num_moved)
0843          {
0844              if (!in_array($cur_folder_id, array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX)))
0845              {
0846                  $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
0847                      SET pm_count = pm_count - $num_moved
0848                      WHERE folder_id = $cur_folder_id
0849                          AND user_id = $user_id";
0850                  $db->sql_query($sql);
0851              }
0852   
0853              if ($dest_folder != PRIVMSGS_INBOX)
0854              {
0855                  $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
0856                      SET pm_count = pm_count + $num_moved
0857                      WHERE folder_id = $dest_folder
0858                          AND user_id = $user_id";
0859                  $db->sql_query($sql);
0860              }
0861          }
0862      }
0863      else if (in_array($cur_folder_id, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX)))
0864      {
0865          trigger_error('CANNOT_MOVE_SPECIAL');
0866      }
0867   
0868      return $num_moved;
0869  }
0870   
0871  /**
0872  * Update unread message status
0873  */
0874  function update_unread_status($unread, $msg_id, $user_id, $folder_id)
0875  {
0876      if (!$unread)
0877      {
0878          return;
0879      }
0880   
0881      global $db, $user, $phpbb_container;
0882   
0883      /* @var $phpbb_notifications \phpbb\notification\manager */
0884      $phpbb_notifications = $phpbb_container->get('notification_manager');
0885   
0886      $phpbb_notifications->mark_notifications('notification.type.pm', $msg_id, $user_id);
0887   
0888      $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
0889          SET pm_unread = 0
0890          WHERE msg_id = $msg_id
0891              AND user_id = $user_id
0892              AND folder_id = $folder_id
0893              AND pm_unread = 1";
0894      $db->sql_query($sql);
0895   
0896      // If the message is already marked as read, we just skip the rest to avoid negative PM count
0897      if (!$db->sql_affectedrows())
0898      {
0899          return;
0900      }
0901   
0902      $sql = 'UPDATE ' . USERS_TABLE . "
0903          SET user_unread_privmsg = user_unread_privmsg - 1
0904          WHERE user_id = $user_id";
0905      $db->sql_query($sql);
0906   
0907      if ($user->data['user_id'] == $user_id)
0908      {
0909          $user->data['user_unread_privmsg']--;
0910   
0911          // Try to cope with previous wrong conversions...
0912          if ($user->data['user_unread_privmsg'] < 0)
0913          {
0914              $sql = 'UPDATE ' . USERS_TABLE . "
0915                  SET user_unread_privmsg = 0
0916                  WHERE user_id = $user_id";
0917              $db->sql_query($sql);
0918   
0919              $user->data['user_unread_privmsg'] = 0;
0920          }
0921      }
0922  }
0923   
0924  function mark_folder_read($user_id, $folder_id)
0925  {
0926      global $db;
0927   
0928      $sql = 'SELECT msg_id
0929          FROM ' . PRIVMSGS_TO_TABLE . '
0930          WHERE folder_id = ' . ((int) $folder_id) . '
0931              AND user_id = ' . ((int) $user_id) . '
0932              AND pm_unread = 1';
0933      $result = $db->sql_query($sql);
0934   
0935      while ($row = $db->sql_fetchrow($result))
0936      {
0937          update_unread_status(true, $row['msg_id'], $user_id, $folder_id);
0938      }
0939      $db->sql_freeresult($result);
0940  }
0941   
0942  /**
0943  * Handle all actions possible with marked messages
0944  */
0945  function handle_mark_actions($user_id, $mark_action)
0946  {
0947      global $db, $user, $phpbb_root_path, $phpEx, $request;
0948   
0949      $msg_ids        = $request->variable('marked_msg_id', array(0));
0950      $cur_folder_id    = $request->variable('cur_folder_id', PRIVMSGS_NO_BOX);
0951   
0952      if (!sizeof($msg_ids))
0953      {
0954          return false;
0955      }
0956   
0957      switch ($mark_action)
0958      {
0959          case 'mark_important':
0960   
0961              $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
0962                  SET pm_marked = 1 - pm_marked
0963                  WHERE folder_id = $cur_folder_id
0964                      AND user_id = $user_id
0965                      AND " . $db->sql_in_set('msg_id', $msg_ids);
0966              $db->sql_query($sql);
0967   
0968          break;
0969   
0970          case 'delete_marked':
0971   
0972              global $auth;
0973   
0974              if (!$auth->acl_get('u_pm_delete'))
0975              {
0976                  send_status_line(403, 'Forbidden');
0977                  trigger_error('NO_AUTH_DELETE_MESSAGE');
0978              }
0979   
0980              if (confirm_box(true))
0981              {
0982                  delete_pm($user_id, $msg_ids, $cur_folder_id);
0983   
0984                  $success_msg = (sizeof($msg_ids) == 1) ? 'MESSAGE_DELETED' : 'MESSAGES_DELETED';
0985                  $redirect = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=' . $cur_folder_id);
0986   
0987                  meta_refresh(3, $redirect);
0988                  trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_FOLDER'], '<a href="' . $redirect . '">', '</a>'));
0989              }
0990              else
0991              {
0992                  $s_hidden_fields = array(
0993                      'cur_folder_id'    => $cur_folder_id,
0994                      'mark_option'    => 'delete_marked',
0995                      'submit_mark'    => true,
0996                      'marked_msg_id'    => $msg_ids
0997                  );
0998   
0999                  confirm_box(false, 'DELETE_MARKED_PM', build_hidden_fields($s_hidden_fields));
1000              }
1001   
1002          break;
1003   
1004          default:
1005              return false;
1006      }
1007   
1008      return true;
1009  }
1010   
1011  /**
1012  * Delete PM(s)
1013  */
1014  function delete_pm($user_id, $msg_ids, $folder_id)
1015  {
1016      global $db, $user, $phpbb_container, $phpbb_dispatcher;
1017   
1018      $user_id    = (int) $user_id;
1019      $folder_id    = (int) $folder_id;
1020   
1021      if (!$user_id)
1022      {
1023          return false;
1024      }
1025   
1026      if (!is_array($msg_ids))
1027      {
1028          if (!$msg_ids)
1029          {
1030              return false;
1031          }
1032          $msg_ids = array($msg_ids);
1033      }
1034   
1035      if (!sizeof($msg_ids))
1036      {
1037          return false;
1038      }
1039   
1040      /**
1041      * Get all info for PM(s) before they are deleted
1042      *
1043      * @event core.delete_pm_before
1044      * @var    int    user_id     ID of the user requested the message delete
1045      * @var    array    msg_ids    array of all messages to be deleted
1046      * @var    int    folder_id    ID of the user folder where the messages are stored
1047      * @since 3.1.0-b5
1048      */
1049      $vars = array('user_id', 'msg_ids', 'folder_id');
1050      extract($phpbb_dispatcher->trigger_event('core.delete_pm_before', compact($vars)));
1051   
1052      // Get PM Information for later deleting
1053      $sql = 'SELECT msg_id, pm_unread, pm_new
1054          FROM ' . PRIVMSGS_TO_TABLE . '
1055          WHERE ' . $db->sql_in_set('msg_id', array_map('intval', $msg_ids)) . "
1056              AND folder_id = $folder_id
1057              AND user_id = $user_id";
1058      $result = $db->sql_query($sql);
1059   
1060      $delete_rows = array();
1061      $num_unread = $num_new = $num_deleted = 0;
1062      while ($row = $db->sql_fetchrow($result))
1063      {
1064          $num_unread += (int) $row['pm_unread'];
1065          $num_new += (int) $row['pm_new'];
1066   
1067          $delete_rows[$row['msg_id']] = 1;
1068      }
1069      $db->sql_freeresult($result);
1070      unset($msg_ids);
1071   
1072      if (!sizeof($delete_rows))
1073      {
1074          return false;
1075      }
1076   
1077      $db->sql_transaction('begin');
1078   
1079      // if no one has read the message yet (meaning it is in users outbox)
1080      // then mark the message as deleted...
1081      if ($folder_id == PRIVMSGS_OUTBOX)
1082      {
1083          // Remove PM from Outbox
1084          $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . "
1085              WHERE user_id = $user_id AND folder_id = " . PRIVMSGS_OUTBOX . '
1086                  AND ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1087          $db->sql_query($sql);
1088   
1089          // Update PM Information for safety
1090          $sql = 'UPDATE ' . PRIVMSGS_TABLE . " SET message_text = ''
1091              WHERE " . $db->sql_in_set('msg_id', array_keys($delete_rows));
1092          $db->sql_query($sql);
1093   
1094          // Set delete flag for those intended to receive the PM
1095          // We do not remove the message actually, to retain some basic information (sent time for example)
1096          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1097              SET pm_deleted = 1
1098              WHERE ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1099          $db->sql_query($sql);
1100   
1101          $num_deleted = $db->sql_affectedrows();
1102      }
1103      else
1104      {
1105          // Delete private message data
1106          $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . "
1107              WHERE user_id = $user_id
1108                  AND folder_id = $folder_id
1109                  AND " . $db->sql_in_set('msg_id', array_keys($delete_rows));
1110          $db->sql_query($sql);
1111          $num_deleted = $db->sql_affectedrows();
1112      }
1113   
1114      // if folder id is user defined folder then decrease pm_count
1115      if (!in_array($folder_id, array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX, PRIVMSGS_NO_BOX)))
1116      {
1117          $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
1118              SET pm_count = pm_count - $num_deleted
1119              WHERE folder_id = $folder_id";
1120          $db->sql_query($sql);
1121      }
1122   
1123      // Update unread and new status field
1124      if ($num_unread || $num_new)
1125      {
1126          $set_sql = ($num_unread) ? 'user_unread_privmsg = user_unread_privmsg - ' . $num_unread : '';
1127   
1128          if ($num_new)
1129          {
1130              $set_sql .= ($set_sql != '') ? ', ' : '';
1131              $set_sql .= 'user_new_privmsg = user_new_privmsg - ' . $num_new;
1132          }
1133   
1134          $db->sql_query('UPDATE ' . USERS_TABLE . " SET $set_sql WHERE user_id = $user_id");
1135   
1136          $user->data['user_new_privmsg'] -= $num_new;
1137          $user->data['user_unread_privmsg'] -= $num_unread;
1138      }
1139   
1140      /* @var $phpbb_notifications \phpbb\notification\manager */
1141      $phpbb_notifications = $phpbb_container->get('notification_manager');
1142   
1143      $phpbb_notifications->delete_notifications('notification.type.pm', array_keys($delete_rows));
1144   
1145      // Now we have to check which messages we can delete completely
1146      $sql = 'SELECT msg_id
1147          FROM ' . PRIVMSGS_TO_TABLE . '
1148          WHERE ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1149      $result = $db->sql_query($sql);
1150   
1151      while ($row = $db->sql_fetchrow($result))
1152      {
1153          unset($delete_rows[$row['msg_id']]);
1154      }
1155      $db->sql_freeresult($result);
1156   
1157      $delete_ids = array_keys($delete_rows);
1158   
1159      if (sizeof($delete_ids))
1160      {
1161          // Check if there are any attachments we need to remove
1162          /** @var \phpbb\attachment\manager $attachment_manager */
1163          $attachment_manager = $phpbb_container->get('attachment.manager');
1164          $attachment_manager->delete('message', $delete_ids, false);
1165          unset($attachment_manager);
1166   
1167          $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1168              WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1169          $db->sql_query($sql);
1170      }
1171   
1172      $db->sql_transaction('commit');
1173   
1174      return true;
1175  }
1176   
1177  /**
1178  * Delete all PM(s) for a given user and delete the ones without references
1179  *
1180  * @param    int        $user_id    ID of the user whose private messages we want to delete
1181  *
1182  * @return    boolean        False if there were no pms found, true otherwise.
1183  */
1184  function phpbb_delete_user_pms($user_id)
1185  {
1186      $user_id = (int) $user_id;
1187   
1188      if (!$user_id)
1189      {
1190          return false;
1191      }
1192   
1193      return phpbb_delete_users_pms(array($user_id));
1194  }
1195   
1196  /**
1197  * Delete all PM(s) for given users and delete the ones without references
1198  *
1199  * @param    array        $user_ids    IDs of the users whose private messages we want to delete
1200  *
1201  * @return    boolean        False if there were no pms found, true otherwise.
1202  */
1203  function phpbb_delete_users_pms($user_ids)
1204  {
1205      global $db, $phpbb_container;
1206   
1207      $user_id_sql = $db->sql_in_set('user_id', $user_ids);
1208      $author_id_sql = $db->sql_in_set('author_id', $user_ids);
1209   
1210      // Get PM Information for later deleting
1211      // The two queries where split, so we can use our indexes
1212      $undelivered_msg = $delete_ids = array();
1213   
1214      // Part 1: get PMs the user received
1215      $sql = 'SELECT msg_id
1216          FROM ' . PRIVMSGS_TO_TABLE . '
1217          WHERE ' . $user_id_sql;
1218      $result = $db->sql_query($sql);
1219   
1220      while ($row = $db->sql_fetchrow($result))
1221      {
1222          $msg_id = (int) $row['msg_id'];
1223          $delete_ids[$msg_id] = $msg_id;
1224      }
1225      $db->sql_freeresult($result);
1226   
1227      // Part 2: get PMs the users sent, but are yet to be received.
1228      // We cannot simply delete them. First we have to check
1229      // whether another user already received and read the message.
1230      $sql = 'SELECT msg_id
1231          FROM ' . PRIVMSGS_TO_TABLE . '
1232          WHERE ' . $author_id_sql . '
1233              AND folder_id = ' . PRIVMSGS_NO_BOX;
1234      $result = $db->sql_query($sql);
1235   
1236      while ($row = $db->sql_fetchrow($result))
1237      {
1238          $msg_id = (int) $row['msg_id'];
1239          $undelivered_msg[$msg_id] = $msg_id;
1240      }
1241      $db->sql_freeresult($result);
1242   
1243      if (empty($delete_ids) && empty($undelivered_msg))
1244      {
1245          return false;
1246      }
1247   
1248      $db->sql_transaction('begin');
1249   
1250      /* @var $phpbb_notifications \phpbb\notification\manager */
1251      $phpbb_notifications = $phpbb_container->get('notification_manager');
1252   
1253      if (!empty($undelivered_msg))
1254      {
1255          // A pm is delivered, if for any recipient the message was moved
1256          // from their NO_BOX to another folder. We do not delete such
1257          // messages, but only delete them for users, who have not yet
1258          // received them.
1259          $sql = 'SELECT msg_id
1260              FROM ' . PRIVMSGS_TO_TABLE . '
1261              WHERE ' . $author_id_sql . '
1262                  AND folder_id <> ' . PRIVMSGS_NO_BOX . '
1263                  AND folder_id <> ' . PRIVMSGS_OUTBOX . '
1264                  AND folder_id <> ' . PRIVMSGS_SENTBOX;
1265          $result = $db->sql_query($sql);
1266   
1267          $delivered_msg = array();
1268          while ($row = $db->sql_fetchrow($result))
1269          {
1270              $msg_id = (int) $row['msg_id'];
1271              $delivered_msg[$msg_id] = $msg_id;
1272              unset($undelivered_msg[$msg_id]);
1273          }
1274          $db->sql_freeresult($result);
1275   
1276          $undelivered_user = array();
1277   
1278          // Count the messages we delete, so we can correct the user pm data
1279          $sql = 'SELECT user_id, COUNT(msg_id) as num_undelivered_privmsgs
1280              FROM ' . PRIVMSGS_TO_TABLE . '
1281              WHERE ' . $author_id_sql . '
1282                  AND folder_id = ' . PRIVMSGS_NO_BOX . '
1283                      AND ' . $db->sql_in_set('msg_id', array_merge($undelivered_msg, $delivered_msg)) . '
1284              GROUP BY user_id';
1285          $result = $db->sql_query($sql);
1286   
1287          while ($row = $db->sql_fetchrow($result))
1288          {
1289              $num_pms = (int) $row['num_undelivered_privmsgs'];
1290              $undelivered_user[$num_pms][] = (int) $row['user_id'];
1291   
1292              if (sizeof($undelivered_user[$num_pms]) > 50)
1293              {
1294                  // If there are too many users affected the query might get
1295                  // too long, so we update the value for the first bunch here.
1296                  $sql = 'UPDATE ' . USERS_TABLE . '
1297                      SET user_new_privmsg = user_new_privmsg - ' . $num_pms . ',
1298                          user_unread_privmsg = user_unread_privmsg - ' . $num_pms . '
1299                      WHERE ' . $db->sql_in_set('user_id', $undelivered_user[$num_pms]);
1300                  $db->sql_query($sql);
1301                  unset($undelivered_user[$num_pms]);
1302              }
1303          }
1304          $db->sql_freeresult($result);
1305   
1306          foreach ($undelivered_user as $num_pms => $undelivered_user_set)
1307          {
1308              $sql = 'UPDATE ' . USERS_TABLE . '
1309                  SET user_new_privmsg = user_new_privmsg - ' . $num_pms . ',
1310                      user_unread_privmsg = user_unread_privmsg - ' . $num_pms . '
1311                  WHERE ' . $db->sql_in_set('user_id', $undelivered_user_set);
1312              $db->sql_query($sql);
1313          }
1314   
1315          if (!empty($delivered_msg))
1316          {
1317              $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
1318                  WHERE folder_id = ' . PRIVMSGS_NO_BOX . '
1319                      AND ' . $db->sql_in_set('msg_id', $delivered_msg);
1320              $db->sql_query($sql);
1321   
1322              $phpbb_notifications->delete_notifications('notification.type.pm', $delivered_msg);
1323          }
1324   
1325          if (!empty($undelivered_msg))
1326          {
1327              $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
1328                  WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
1329              $db->sql_query($sql);
1330   
1331              $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1332                  WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
1333              $db->sql_query($sql);
1334   
1335              $phpbb_notifications->delete_notifications('notification.type.pm', $undelivered_msg);
1336          }
1337      }
1338   
1339      // Reset the user's pm count to 0
1340      $sql = 'UPDATE ' . USERS_TABLE . '
1341          SET user_new_privmsg = 0,
1342              user_unread_privmsg = 0
1343          WHERE ' . $user_id_sql;
1344      $db->sql_query($sql);
1345   
1346      // Delete private message data of the user
1347      $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
1348          WHERE ' . $user_id_sql;
1349      $db->sql_query($sql);
1350   
1351      if (!empty($delete_ids))
1352      {
1353          // Now we have to check which messages we can delete completely
1354          $sql = 'SELECT msg_id
1355              FROM ' . PRIVMSGS_TO_TABLE . '
1356              WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1357          $result = $db->sql_query($sql);
1358   
1359          while ($row = $db->sql_fetchrow($result))
1360          {
1361              unset($delete_ids[$row['msg_id']]);
1362          }
1363          $db->sql_freeresult($result);
1364   
1365          if (!empty($delete_ids))
1366          {
1367              // Check if there are any attachments we need to remove
1368              /** @var \phpbb\attachment\manager $attachment_manager */
1369              $attachment_manager = $phpbb_container->get('attachment.manager');
1370              $attachment_manager->delete('message', $delete_ids, false);
1371              unset($attachment_manager);
1372   
1373              $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1374                  WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1375              $db->sql_query($sql);
1376   
1377              $phpbb_notifications->delete_notifications('notification.type.pm', $delete_ids);
1378          }
1379      }
1380   
1381      // Set the remaining author id to anonymous
1382      // This way users are still able to read messages from users being removed
1383      $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1384          SET author_id = ' . ANONYMOUS . '
1385          WHERE ' . $author_id_sql;
1386      $db->sql_query($sql);
1387   
1388      $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1389          SET author_id = ' . ANONYMOUS . '
1390          WHERE ' . $author_id_sql;
1391      $db->sql_query($sql);
1392   
1393      $db->sql_transaction('commit');
1394   
1395      return true;
1396  }
1397   
1398  /**
1399  * Rebuild message header
1400  */
1401  function rebuild_header($check_ary)
1402  {
1403      $address = array();
1404   
1405      foreach ($check_ary as $check_type => $address_field)
1406      {
1407          // Split Addresses into users and groups
1408          preg_match_all('/:?(u|g)_([0-9]+):?/', $address_field, $match);
1409   
1410          $u = $g = array();
1411          foreach ($match[1] as $id => $type)
1412          {
1413              ${$type}[] = (int) $match[2][$id];
1414          }
1415   
1416          $_types = array('u', 'g');
1417          foreach ($_types as $type)
1418          {
1419              if (sizeof(${$type}))
1420              {
1421                  foreach (${$type} as $id)
1422                  {
1423                      $address[$type][$id] = $check_type;
1424                  }
1425              }
1426          }
1427      }
1428   
1429      return $address;
1430  }
1431   
1432  /**
1433  * Print out/assign recipient information
1434  */
1435  function write_pm_addresses($check_ary, $author_id, $plaintext = false)
1436  {
1437      global $db, $user, $template, $phpbb_root_path, $phpEx, $phpbb_container;
1438   
1439      /** @var \phpbb\group\helper $group_helper */
1440      $group_helper = $phpbb_container->get('group_helper');
1441   
1442      $addresses = array();
1443   
1444      foreach ($check_ary as $check_type => $address_field)
1445      {
1446          if (!is_array($address_field))
1447          {
1448              // Split Addresses into users and groups
1449              preg_match_all('/:?(u|g)_([0-9]+):?/', $address_field, $match);
1450   
1451              $u = $g = array();
1452              foreach ($match[1] as $id => $type)
1453              {
1454                  ${$type}[] = (int) $match[2][$id];
1455              }
1456          }
1457          else
1458          {
1459              $u = $address_field['u'];
1460              $g = $address_field['g'];
1461          }
1462   
1463          $address = array();
1464          if (sizeof($u))
1465          {
1466              $sql = 'SELECT user_id, username, user_colour
1467                  FROM ' . USERS_TABLE . '
1468                  WHERE ' . $db->sql_in_set('user_id', $u);
1469              $result = $db->sql_query($sql);
1470   
1471              while ($row = $db->sql_fetchrow($result))
1472              {
1473                  if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1474                  {
1475                      if ($plaintext)
1476                      {
1477                          $address[] = $row['username'];
1478                      }
1479                      else
1480                      {
1481                          $address['user'][$row['user_id']] = array('name' => $row['username'], 'colour' => $row['user_colour']);
1482                      }
1483                  }
1484              }
1485              $db->sql_freeresult($result);
1486          }
1487   
1488          if (sizeof($g))
1489          {
1490              if ($plaintext)
1491              {
1492                  $sql = 'SELECT group_name, group_type
1493                      FROM ' . GROUPS_TABLE . '
1494                          WHERE ' . $db->sql_in_set('group_id', $g);
1495                  $result = $db->sql_query($sql);
1496   
1497                  while ($row = $db->sql_fetchrow($result))
1498                  {
1499                      if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1500                      {
1501                          $address[] = $group_helper->get_name($row['group_name']);
1502                      }
1503                  }
1504                  $db->sql_freeresult($result);
1505              }
1506              else
1507              {
1508                  $sql = 'SELECT g.group_id, g.group_name, g.group_colour, g.group_type, ug.user_id
1509                      FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
1510                          WHERE ' . $db->sql_in_set('g.group_id', $g) . '
1511                          AND g.group_id = ug.group_id
1512                          AND ug.user_pending = 0';
1513                  $result = $db->sql_query($sql);
1514   
1515                  while ($row = $db->sql_fetchrow($result))
1516                  {
1517                      if (!isset($address['group'][$row['group_id']]))
1518                      {
1519                          if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1520                          {
1521                              $row['group_name'] = $group_helper->get_name($row['group_name']);
1522                              $address['group'][$row['group_id']] = array('name' => $row['group_name'], 'colour' => $row['group_colour']);
1523                          }
1524                      }
1525   
1526                      if (isset($address['user'][$row['user_id']]))
1527                      {
1528                          $address['user'][$row['user_id']]['in_group'] = $row['group_id'];
1529                      }
1530                  }
1531                  $db->sql_freeresult($result);
1532              }
1533          }
1534   
1535          if (sizeof($address) && !$plaintext)
1536          {
1537              $template->assign_var('S_' . strtoupper($check_type) . '_RECIPIENT', true);
1538   
1539              foreach ($address as $type => $adr_ary)
1540              {
1541                  foreach ($adr_ary as $id => $row)
1542                  {
1543                      $tpl_ary = array(
1544                          'IS_GROUP'    => ($type == 'group') ? true : false,
1545                          'IS_USER'    => ($type == 'user') ? true : false,
1546                          'UG_ID'        => $id,
1547                          'NAME'        => $row['name'],
1548                          'COLOUR'    => ($row['colour']) ? '#' . $row['colour'] : '',
1549                          'TYPE'        => $type,
1550                      );
1551   
1552                      if ($type == 'user')
1553                      {
1554                          $tpl_ary = array_merge($tpl_ary, array(
1555                              'U_VIEW'        => get_username_string('profile', $id, $row['name'], $row['colour']),
1556                              'NAME_FULL'        => get_username_string('full', $id, $row['name'], $row['colour']),
1557                          ));
1558                      }
1559                      else
1560                      {
1561                          $tpl_ary = array_merge($tpl_ary, array(
1562                              'U_VIEW'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $id),
1563                          ));
1564                      }
1565   
1566                      $template->assign_block_vars($check_type . '_recipient', $tpl_ary);
1567                  }
1568              }
1569          }
1570   
1571          $addresses[$check_type] = $address;
1572      }
1573   
1574      return $addresses;
1575  }
1576   
1577  /**
1578  * Get folder status
1579  */
1580  function get_folder_status($folder_id, $folder)
1581  {
1582      global $user;
1583   
1584      if (isset($folder[$folder_id]))
1585      {
1586          $folder = $folder[$folder_id];
1587      }
1588      else
1589      {
1590          return false;
1591      }
1592   
1593      $return = array(
1594          'folder_name'    => $folder['folder_name'],
1595          'cur'            => $folder['num_messages'],
1596          'remaining'        => ($user->data['message_limit']) ? $user->data['message_limit'] - $folder['num_messages'] : 0,
1597          'max'            => $user->data['message_limit'],
1598          'percent'        => ($user->data['message_limit']) ? (($user->data['message_limit'] > 0) ? floor(($folder['num_messages'] / $user->data['message_limit']) * 100) : 100) : 0,
1599      );
1600   
1601      $return['message']    = $user->lang('FOLDER_STATUS_MSG', $user->lang('MESSAGES_COUNT', (int) $return['max']), (int) $return['cur'], $return['percent']);
1602   
1603      return $return;
1604  }
1605   
1606  //
1607  // COMPOSE MESSAGES
1608  //
1609   
1610  /**
1611  * Submit PM
1612  */
1613  function submit_pm($mode, $subject, &$data_ary, $put_in_outbox = true)
1614  {
1615      global $db, $auth, $config, $user, $phpbb_root_path, $phpbb_container, $phpbb_dispatcher, $request;
1616   
1617      // We do not handle erasing pms here
1618      if ($mode == 'delete')
1619      {
1620          return false;
1621      }
1622   
1623      $current_time = time();
1624   
1625      $data = $data_ary;
1626      /**
1627      * Get all parts of the PM that are to be submited to the DB.
1628      *
1629      * @event core.submit_pm_before
1630      * @var    string    mode    PM Post mode - post|reply|quote|quotepost|forward|edit
1631      * @var    string    subject    Subject of the private message
1632      * @var    array    data    The whole row data of the PM.
1633      * @since 3.1.0-b3
1634      */
1635      $vars = array('mode', 'subject', 'data');
1636      extract($phpbb_dispatcher->trigger_event('core.submit_pm_before', compact($vars)));
1637      $data_ary = $data;
1638      unset($data);
1639   
1640      // Collect some basic information about which tables and which rows to update/insert
1641      $sql_data = array();
1642      $root_level = 0;
1643   
1644      // Recipient Information
1645      $recipients = $to = $bcc = array();
1646   
1647      if ($mode != 'edit')
1648      {
1649          // Build Recipient List
1650          // u|g => array($user_id => 'to'|'bcc')
1651          $_types = array('u', 'g');
1652          foreach ($_types as $ug_type)
1653          {
1654              if (isset($data_ary['address_list'][$ug_type]) && sizeof($data_ary['address_list'][$ug_type]))
1655              {
1656                  foreach ($data_ary['address_list'][$ug_type] as $id => $field)
1657                  {
1658                      $id = (int) $id;
1659   
1660                      // Do not rely on the address list being "valid"
1661                      if (!$id || ($ug_type == 'u' && $id == ANONYMOUS))
1662                      {
1663                          continue;
1664                      }
1665   
1666                      $field = ($field == 'to') ? 'to' : 'bcc';
1667                      if ($ug_type == 'u')
1668                      {
1669                          $recipients[$id] = $field;
1670                      }
1671                      ${$field}[] = $ug_type . '_' . $id;
1672                  }
1673              }
1674          }
1675   
1676          if (isset($data_ary['address_list']['g']) && sizeof($data_ary['address_list']['g']))
1677          {
1678              // We need to check the PM status of group members (do they want to receive PM's?)
1679              // Only check if not a moderator or admin, since they are allowed to override this user setting
1680              $sql_allow_pm = (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) ? ' AND u.user_allow_pm = 1' : '';
1681   
1682              $sql = 'SELECT u.user_type, ug.group_id, ug.user_id
1683                  FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
1684                  WHERE ' . $db->sql_in_set('ug.group_id', array_keys($data_ary['address_list']['g'])) . '
1685                      AND ug.user_pending = 0
1686                      AND u.user_id = ug.user_id
1687                      AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')' .
1688                      $sql_allow_pm;
1689              $result = $db->sql_query($sql);
1690   
1691              while ($row = $db->sql_fetchrow($result))
1692              {
1693                  $field = ($data_ary['address_list']['g'][$row['group_id']] == 'to') ? 'to' : 'bcc';
1694                  $recipients[$row['user_id']] = $field;
1695              }
1696              $db->sql_freeresult($result);
1697          }
1698   
1699          if (!sizeof($recipients))
1700          {
1701              trigger_error('NO_RECIPIENT');
1702          }
1703      }
1704   
1705      // First of all make sure the subject are having the correct length.
1706      $subject = truncate_string($subject);
1707   
1708      $db->sql_transaction('begin');
1709   
1710      $sql = '';
1711   
1712      switch ($mode)
1713      {
1714          case 'reply':
1715          case 'quote':
1716              $root_level = ($data_ary['reply_from_root_level']) ? $data_ary['reply_from_root_level'] : $data_ary['reply_from_msg_id'];
1717   
1718              // Set message_replied switch for this user
1719              $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1720                  SET pm_replied = 1
1721                  WHERE user_id = ' . $data_ary['from_user_id'] . '
1722                      AND msg_id = ' . $data_ary['reply_from_msg_id'];
1723   
1724          // no break
1725   
1726          case 'forward':
1727          case 'post':
1728          case 'quotepost':
1729              $sql_data = array(
1730                  'root_level'        => $root_level,
1731                  'author_id'            => $data_ary['from_user_id'],
1732                  'icon_id'            => $data_ary['icon_id'],
1733                  'author_ip'            => $data_ary['from_user_ip'],
1734                  'message_time'        => $current_time,
1735                  'enable_bbcode'        => $data_ary['enable_bbcode'],
1736                  'enable_smilies'    => $data_ary['enable_smilies'],
1737                  'enable_magic_url'    => $data_ary['enable_urls'],
1738                  'enable_sig'        => $data_ary['enable_sig'],
1739                  'message_subject'    => $subject,
1740                  'message_text'        => $data_ary['message'],
1741                  'message_attachment'=> (!empty($data_ary['attachment_data'])) ? 1 : 0,
1742                  'bbcode_bitfield'    => $data_ary['bbcode_bitfield'],
1743                  'bbcode_uid'        => $data_ary['bbcode_uid'],
1744                  'to_address'        => implode(':', $to),
1745                  'bcc_address'        => implode(':', $bcc),
1746                  'message_reported'    => 0,
1747              );
1748          break;
1749   
1750          case 'edit':
1751              $sql_data = array(
1752                  'icon_id'            => $data_ary['icon_id'],
1753                  'message_edit_time'    => $current_time,
1754                  'enable_bbcode'        => $data_ary['enable_bbcode'],
1755                  'enable_smilies'    => $data_ary['enable_smilies'],
1756                  'enable_magic_url'    => $data_ary['enable_urls'],
1757                  'enable_sig'        => $data_ary['enable_sig'],
1758                  'message_subject'    => $subject,
1759                  'message_text'        => $data_ary['message'],
1760                  'message_attachment'=> (!empty($data_ary['attachment_data'])) ? 1 : 0,
1761                  'bbcode_bitfield'    => $data_ary['bbcode_bitfield'],
1762                  'bbcode_uid'        => $data_ary['bbcode_uid']
1763              );
1764          break;
1765      }
1766   
1767      if (sizeof($sql_data))
1768      {
1769          if ($mode == 'post' || $mode == 'reply' || $mode == 'quote' || $mode == 'quotepost' || $mode == 'forward')
1770          {
1771              $db->sql_query('INSERT INTO ' . PRIVMSGS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_data));
1772              $data_ary['msg_id'] = $db->sql_nextid();
1773          }
1774          else if ($mode == 'edit')
1775          {
1776              $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1777                  SET message_edit_count = message_edit_count + 1, ' . $db->sql_build_array('UPDATE', $sql_data) . '
1778                  WHERE msg_id = ' . $data_ary['msg_id'];
1779              $db->sql_query($sql);
1780          }
1781      }
1782   
1783      if ($mode != 'edit')
1784      {
1785          if ($sql)
1786          {
1787              $db->sql_query($sql);
1788          }
1789          unset($sql);
1790   
1791          $sql_ary = array();
1792          foreach ($recipients as $user_id => $type)
1793          {
1794              $sql_ary[] = array(
1795                  'msg_id'        => (int) $data_ary['msg_id'],
1796                  'user_id'        => (int) $user_id,
1797                  'author_id'        => (int) $data_ary['from_user_id'],
1798                  'folder_id'        => PRIVMSGS_NO_BOX,
1799                  'pm_new'        => 1,
1800                  'pm_unread'        => 1,
1801                  'pm_forwarded'    => ($mode == 'forward') ? 1 : 0
1802              );
1803          }
1804   
1805          $db->sql_multi_insert(PRIVMSGS_TO_TABLE, $sql_ary);
1806   
1807          $sql = 'UPDATE ' . USERS_TABLE . '
1808              SET user_new_privmsg = user_new_privmsg + 1, user_unread_privmsg = user_unread_privmsg + 1, user_last_privmsg = ' . time() . '
1809              WHERE ' . $db->sql_in_set('user_id', array_keys($recipients));
1810          $db->sql_query($sql);
1811   
1812          // Put PM into outbox
1813          if ($put_in_outbox)
1814          {
1815              $db->sql_query('INSERT INTO ' . PRIVMSGS_TO_TABLE . ' ' . $db->sql_build_array('INSERT', array(
1816                  'msg_id'        => (int) $data_ary['msg_id'],
1817                  'user_id'        => (int) $data_ary['from_user_id'],
1818                  'author_id'        => (int) $data_ary['from_user_id'],
1819                  'folder_id'        => PRIVMSGS_OUTBOX,
1820                  'pm_new'        => 0,
1821                  'pm_unread'        => 0,
1822                  'pm_forwarded'    => ($mode == 'forward') ? 1 : 0))
1823              );
1824          }
1825      }
1826   
1827      // Set user last post time
1828      if ($mode == 'reply' || $mode == 'quote' || $mode == 'quotepost' || $mode == 'forward' || $mode == 'post')
1829      {
1830          $sql = 'UPDATE ' . USERS_TABLE . "
1831              SET user_lastpost_time = $current_time
1832              WHERE user_id = " . $data_ary['from_user_id'];
1833          $db->sql_query($sql);
1834      }
1835   
1836      // Submit Attachments
1837      if (!empty($data_ary['attachment_data']) && $data_ary['msg_id'] && in_array($mode, array('post', 'reply', 'quote', 'quotepost', 'edit', 'forward')))
1838      {
1839          $space_taken = $files_added = 0;
1840          $orphan_rows = array();
1841   
1842          foreach ($data_ary['attachment_data'] as $pos => $attach_row)
1843          {
1844              $orphan_rows[(int) $attach_row['attach_id']] = array();
1845          }
1846   
1847          if (sizeof($orphan_rows))
1848          {
1849              $sql = 'SELECT attach_id, filesize, physical_filename
1850                  FROM ' . ATTACHMENTS_TABLE . '
1851                  WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan_rows)) . '
1852                      AND in_message = 1
1853                      AND is_orphan = 1
1854                      AND poster_id = ' . $user->data['user_id'];
1855              $result = $db->sql_query($sql);
1856   
1857              $orphan_rows = array();
1858              while ($row = $db->sql_fetchrow($result))
1859              {
1860                  $orphan_rows[$row['attach_id']] = $row;
1861              }
1862              $db->sql_freeresult($result);
1863          }
1864   
1865          foreach ($data_ary['attachment_data'] as $pos => $attach_row)
1866          {
1867              if ($attach_row['is_orphan'] && !isset($orphan_rows[$attach_row['attach_id']]))
1868              {
1869                  continue;
1870              }
1871   
1872              if (!$attach_row['is_orphan'])
1873              {
1874                  // update entry in db if attachment already stored in db and filespace
1875                  $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
1876                      SET attach_comment = '" . $db->sql_escape($attach_row['attach_comment']) . "'
1877                      WHERE attach_id = " . (int) $attach_row['attach_id'] . '
1878                          AND is_orphan = 0';
1879                  $db->sql_query($sql);
1880              }
1881              else
1882              {
1883                  // insert attachment into db
1884                  if (!@file_exists($phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($orphan_rows[$attach_row['attach_id']]['physical_filename'])))
1885                  {
1886                      continue;
1887                  }
1888   
1889                  $space_taken += $orphan_rows[$attach_row['attach_id']]['filesize'];
1890                  $files_added++;
1891   
1892                  $attach_sql = array(
1893                      'post_msg_id'        => $data_ary['msg_id'],
1894                      'topic_id'            => 0,
1895                      'is_orphan'            => 0,
1896                      'poster_id'            => $data_ary['from_user_id'],
1897                      'attach_comment'    => $attach_row['attach_comment'],
1898                  );
1899   
1900                  $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $attach_sql) . '
1901                      WHERE attach_id = ' . $attach_row['attach_id'] . '
1902                          AND is_orphan = 1
1903                          AND poster_id = ' . $user->data['user_id'];
1904                  $db->sql_query($sql);
1905              }
1906          }
1907   
1908          if ($space_taken && $files_added)
1909          {
1910              $config->increment('upload_dir_size', $space_taken, false);
1911              $config->increment('num_files', $files_added, false);
1912          }
1913      }
1914   
1915      // Delete draft if post was loaded...
1916      $draft_id = $request->variable('draft_loaded', 0);
1917      if ($draft_id)
1918      {
1919          $sql = 'DELETE FROM ' . DRAFTS_TABLE . "
1920              WHERE draft_id = $draft_id
1921                  AND user_id = " . $data_ary['from_user_id'];
1922          $db->sql_query($sql);
1923      }
1924   
1925      $db->sql_transaction('commit');
1926   
1927      // Send Notifications
1928      $pm_data = array_merge($data_ary, array(
1929          'message_subject'        => $subject,
1930          'recipients'            => $recipients,
1931      ));
1932   
1933      /* @var $phpbb_notifications \phpbb\notification\manager */
1934      $phpbb_notifications = $phpbb_container->get('notification_manager');
1935   
1936      if ($mode == 'edit')
1937      {
1938          $phpbb_notifications->update_notifications('notification.type.pm', $pm_data);
1939      }
1940      else
1941      {
1942          $phpbb_notifications->add_notifications('notification.type.pm', $pm_data);
1943      }
1944   
1945      $data = $data_ary;
1946      /**
1947      * Get PM message ID after submission to DB
1948      *
1949      * @event core.submit_pm_after
1950      * @var    string    mode    PM Post mode - post|reply|quote|quotepost|forward|edit
1951      * @var    string    subject    Subject of the private message
1952      * @var    array    data    The whole row data of the PM.
1953      * @var    array    pm_data    The data sent to notification class
1954      * @since 3.1.0-b5
1955      */
1956      $vars = array('mode', 'subject', 'data', 'pm_data');
1957      extract($phpbb_dispatcher->trigger_event('core.submit_pm_after', compact($vars)));
1958      $data_ary = $data;
1959      unset($data);
1960   
1961      return $data_ary['msg_id'];
1962  }
1963   
1964  /**
1965  * Display Message History
1966  */
1967  function message_history($msg_id, $user_id, $message_row, $folder, $in_post_mode = false)
1968  {
1969      global $db, $user, $template, $phpbb_root_path, $phpEx, $auth;
1970   
1971      // Select all receipts and the author from the pm we currently view, to only display their pm-history
1972      $sql = 'SELECT author_id, user_id
1973          FROM ' . PRIVMSGS_TO_TABLE . "
1974          WHERE msg_id = $msg_id
1975              AND folder_id <> " . PRIVMSGS_HOLD_BOX;
1976      $result = $db->sql_query($sql);
1977   
1978      $recipients = array();
1979      while ($row = $db->sql_fetchrow($result))
1980      {
1981          $recipients[] = (int) $row['user_id'];
1982          $recipients[] = (int) $row['author_id'];
1983      }
1984      $db->sql_freeresult($result);
1985      $recipients = array_unique($recipients);
1986   
1987      // Get History Messages (could be newer)
1988      $sql = 'SELECT t.*, p.*, u.*
1989          FROM ' . PRIVMSGS_TABLE . ' p, ' . PRIVMSGS_TO_TABLE . ' t, ' . USERS_TABLE . ' u
1990          WHERE t.msg_id = p.msg_id
1991              AND p.author_id = u.user_id
1992              AND t.folder_id NOT IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ')
1993              AND ' . $db->sql_in_set('t.author_id', $recipients, false, true) . "
1994              AND t.user_id = $user_id";
1995   
1996      // We no longer need those.
1997      unset($recipients);
1998   
1999      if (!$message_row['root_level'])
2000      {
2001          $sql .= " AND (p.root_level = $msg_id OR (p.root_level = 0 AND p.msg_id = $msg_id))";
2002      }
2003      else
2004      {
2005          $sql .= " AND (p.root_level = " . $message_row['root_level'] . ' OR p.msg_id = ' . $message_row['root_level'] . ')';
2006      }
2007      $sql .= ' ORDER BY p.message_time DESC';
2008   
2009      $result = $db->sql_query($sql);
2010      $row = $db->sql_fetchrow($result);
2011   
2012      if (!$row)
2013      {
2014          $db->sql_freeresult($result);
2015          return false;
2016      }
2017   
2018      $title = $row['message_subject'];
2019   
2020      $rowset = array();
2021      $folder_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm') . '&amp;folder=';
2022   
2023      do
2024      {
2025          $folder_id = (int) $row['folder_id'];
2026   
2027          $row['folder'][] = (isset($folder[$folder_id])) ? '<a href="' . $folder_url . $folder_id . '">' . $folder[$folder_id]['folder_name'] . '</a>' : $user->lang['UNKNOWN_FOLDER'];
2028   
2029          if (isset($rowset[$row['msg_id']]))
2030          {
2031              $rowset[$row['msg_id']]['folder'][] = (isset($folder[$folder_id])) ? '<a href="' . $folder_url . $folder_id . '">' . $folder[$folder_id]['folder_name'] . '</a>' : $user->lang['UNKNOWN_FOLDER'];
2032          }
2033          else
2034          {
2035              $rowset[$row['msg_id']] = $row;
2036          }
2037      }
2038      while ($row = $db->sql_fetchrow($result));
2039      $db->sql_freeresult($result);
2040   
2041      if (sizeof($rowset) == 1 && !$in_post_mode)
2042      {
2043          return false;
2044      }
2045   
2046      $title = censor_text($title);
2047   
2048      $url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm');
2049      $next_history_pm = $previous_history_pm = $prev_id = 0;
2050   
2051      // Re-order rowset to be able to get the next/prev message rows...
2052      $rowset = array_values($rowset);
2053   
2054      for ($i = 0, $size = sizeof($rowset); $i < $size; $i++)
2055      {
2056          $row = &$rowset[$i];
2057          $id = (int) $row['msg_id'];
2058   
2059          $author_id    = $row['author_id'];
2060          $folder_id    = (int) $row['folder_id'];
2061   
2062          $subject    = $row['message_subject'];
2063          $message    = $row['message_text'];
2064   
2065          $message = censor_text($message);
2066   
2067          $decoded_message = false;
2068   
2069          if ($in_post_mode && $auth->acl_get('u_sendpm') && $author_id != ANONYMOUS)
2070          {
2071              $decoded_message = $message;
2072              decode_message($decoded_message, $row['bbcode_uid']);
2073   
2074              $decoded_message = bbcode_nl2br($decoded_message);
2075          }
2076   
2077          $parse_flags = ($row['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0);
2078          $parse_flags |= ($row['enable_smilies'] ? OPTION_FLAG_SMILIES : 0);
2079   
2080          $message = generate_text_for_display($message, $row['bbcode_uid'], $row['bbcode_bitfield'], $parse_flags, false);
2081   
2082          $subject = censor_text($subject);
2083   
2084          if ($id == $msg_id)
2085          {
2086              $next_history_pm = (isset($rowset[$i + 1])) ? (int) $rowset[$i + 1]['msg_id'] : 0;
2087              $previous_history_pm = $prev_id;
2088          }
2089   
2090          $template->assign_block_vars('history_row', array(
2091              'MESSAGE_AUTHOR_QUOTE'        => (($decoded_message) ? addslashes(get_username_string('username', $author_id, $row['username'], $row['user_colour'], $row['username'])) : ''),
2092              'MESSAGE_AUTHOR_FULL'        => get_username_string('full', $author_id, $row['username'], $row['user_colour'], $row['username']),
2093              'MESSAGE_AUTHOR_COLOUR'        => get_username_string('colour', $author_id, $row['username'], $row['user_colour'], $row['username']),
2094              'MESSAGE_AUTHOR'            => get_username_string('username', $author_id, $row['username'], $row['user_colour'], $row['username']),
2095              'U_MESSAGE_AUTHOR'            => get_username_string('profile', $author_id, $row['username'], $row['user_colour'], $row['username']),
2096   
2097              'SUBJECT'            => $subject,
2098              'SENT_DATE'            => $user->format_date($row['message_time']),
2099              'MESSAGE'            => $message,
2100              'FOLDER'            => implode($user->lang['COMMA_SEPARATOR'], $row['folder']),
2101              'DECODED_MESSAGE'    => $decoded_message,
2102   
2103              'S_CURRENT_MSG'        => ($row['msg_id'] == $msg_id),
2104              'S_AUTHOR_DELETED'    => ($author_id == ANONYMOUS) ? true : false,
2105              'S_IN_POST_MODE'    => $in_post_mode,
2106   
2107              'MSG_ID'            => $row['msg_id'],
2108              'MESSAGE_TIME'        => $row['message_time'],
2109              'USER_ID'            => $row['user_id'],
2110              'U_VIEW_MESSAGE'    => "$url&amp;f=$folder_id&amp;p=" . $row['msg_id'],
2111              'U_QUOTE'            => (!$in_post_mode && $auth->acl_get('u_sendpm') && $author_id != ANONYMOUS) ? "$url&amp;mode=compose&amp;action=quote&amp;f=" . $folder_id . "&amp;p=" . $row['msg_id'] : '',
2112              'U_POST_REPLY_PM'    => ($author_id != $user->data['user_id'] && $author_id != ANONYMOUS && $auth->acl_get('u_sendpm')) ? "$url&amp;mode=compose&amp;action=reply&amp;f=$folder_id&amp;p=" . $row['msg_id'] : '')
2113          );
2114          unset($rowset[$i]);
2115          $prev_id = $id;
2116      }
2117   
2118      $template->assign_vars(array(
2119          'QUOTE_IMG'            => $user->img('icon_post_quote', $user->lang['REPLY_WITH_QUOTE']),
2120          'HISTORY_TITLE'        => $title,
2121   
2122          'U_VIEW_NEXT_HISTORY'        => ($next_history_pm) ? "$url&amp;p=" . $next_history_pm : '',
2123          'U_VIEW_PREVIOUS_HISTORY'    => ($previous_history_pm) ? "$url&amp;p=" . $previous_history_pm : '',
2124      ));
2125   
2126      return true;
2127  }
2128   
2129  /**
2130  * Set correct users max messages in PM folder.
2131  * If several group memberships define different amount of messages, the highest will be chosen.
2132  */
2133  function set_user_message_limit()
2134  {
2135      global $user, $db, $config;
2136   
2137      // Get maximum about from user memberships
2138      $message_limit = phpbb_get_max_setting_from_group($db, $user->data['user_id'], 'message_limit');
2139   
2140      // If it is 0, there is no limit set and we use the maximum value within the config.
2141      $user->data['message_limit'] = (!$message_limit) ? $config['pm_max_msgs'] : $message_limit;
2142  }
2143   
2144  /**
2145   * Get the maximum PM setting for the groups of the user
2146   *
2147   * @param \phpbb\db\driver\driver_interface $db
2148   * @param int $user_id
2149   * @param string $setting Only 'max_recipients' and 'message_limit' are supported
2150   * @return int The maximum setting for all groups of the user, unless one group has '0'
2151   * @throws \InvalidArgumentException If selected group setting is not supported
2152   */
2153  function phpbb_get_max_setting_from_group(\phpbb\db\driver\driver_interface $db, $user_id, $setting)
2154  {
2155      if ($setting !== 'max_recipients' && $setting !== 'message_limit')
2156      {
2157          throw new InvalidArgumentException('Setting "' . $setting . '" is not supported');
2158      }
2159   
2160      // Get maximum number of allowed recipients
2161      $sql = 'SELECT MAX(g.group_' . $setting . ') as max_setting
2162          FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
2163          WHERE ug.user_id = ' . (int) $user_id . '
2164              AND ug.user_pending = 0
2165              AND ug.group_id = g.group_id';
2166      $result = $db->sql_query($sql);
2167      $row = $db->sql_fetchrow($result);
2168      $db->sql_freeresult($result);
2169      $max_setting = (int) $row['max_setting'];
2170   
2171      return $max_setting;
2172  }
2173   
2174  /**
2175  * Generates an array of coloured recipient names from a list of PMs - (groups & users)
2176  *
2177  * @param    array    $pm_by_id    An array of rows from PRIVMSGS_TABLE, keys are the msg_ids.
2178  *
2179  * @return    array                2D Array: array(msg_id => array('username or group string', ...), ...)
2180  *                                Usernames are generated with {@link get_username_string get_username_string}
2181  *                                Groups are coloured and have a link to the membership page
2182  */
2183  function get_recipient_strings($pm_by_id)
2184  {
2185      global $db, $phpbb_root_path, $phpEx, $user, $phpbb_container;
2186   
2187      /** @var \phpbb\group\helper $group_helper */
2188      $group_helper = $phpbb_container->get('group_helper');
2189   
2190      $address_list = $recipient_list = $address = array();
2191   
2192      $_types = array('u', 'g');
2193   
2194      foreach ($pm_by_id as $message_id => $row)
2195      {
2196          $address[$message_id] = rebuild_header(array('to' => $row['to_address'], 'bcc' => $row['bcc_address']));
2197   
2198          foreach ($_types as $ug_type)
2199          {
2200              if (isset($address[$message_id][$ug_type]) && sizeof($address[$message_id][$ug_type]))
2201              {
2202                  foreach ($address[$message_id][$ug_type] as $ug_id => $in_to)
2203                  {
2204                      $recipient_list[$ug_type][$ug_id] = array('name' => $user->lang['NA'], 'colour' => '');
2205                  }
2206              }
2207          }
2208      }
2209   
2210      foreach ($_types as $ug_type)
2211      {
2212          if (!empty($recipient_list[$ug_type]))
2213          {
2214              if ($ug_type == 'u')
2215              {
2216                  $sql = 'SELECT user_id as id, username as name, user_colour as colour
2217                      FROM ' . USERS_TABLE . '
2218                      WHERE ';
2219              }
2220              else
2221              {
2222                  $sql = 'SELECT group_id as id, group_name as name, group_colour as colour, group_type
2223                      FROM ' . GROUPS_TABLE . '
2224                      WHERE ';
2225              }
2226              $sql .= $db->sql_in_set(($ug_type == 'u') ? 'user_id' : 'group_id', array_map('intval', array_keys($recipient_list[$ug_type])));
2227   
2228              $result = $db->sql_query($sql);
2229   
2230              while ($row = $db->sql_fetchrow($result))
2231              {
2232                  if ($ug_type == 'g')
2233                  {
2234                      $row['name'] = $group_helper->get_name($row['name']);
2235                  }
2236   
2237                  $recipient_list[$ug_type][$row['id']] = array('name' => $row['name'], 'colour' => $row['colour']);
2238              }
2239              $db->sql_freeresult($result);
2240          }
2241      }
2242   
2243      foreach ($address as $message_id => $adr_ary)
2244      {
2245          foreach ($adr_ary as $type => $id_ary)
2246          {
2247              foreach ($id_ary as $ug_id => $_id)
2248              {
2249                  if ($type == 'u')
2250                  {
2251                      $address_list[$message_id][] = get_username_string('full', $ug_id, $recipient_list[$type][$ug_id]['name'], $recipient_list[$type][$ug_id]['colour']);
2252                  }
2253                  else
2254                  {
2255                      $user_colour = ($recipient_list[$type][$ug_id]['colour']) ? ' style="font-weight: bold; color:#' . $recipient_list[$type][$ug_id]['colour'] . '"' : '';
2256                      $link = '<a href="' . append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $ug_id) . '"' . $user_colour . '>';
2257                      $address_list[$message_id][] = $link . $recipient_list[$type][$ug_id]['name'] . (($link) ? '</a>' : '');
2258                  }
2259              }
2260          }
2261      }
2262   
2263      return $address_list;
2264  }
2265