Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:01 - Dateigröße: 66.51 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 (count($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'        => $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 (count($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 (count($delete_ids))
0604      {
0605          $num_removed += count($delete_ids);
0606          delete_pm($user_id, $delete_ids, PRIVMSGS_NO_BOX);
0607      }
0608   
0609      // Set messages to Unread
0610      if (count($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 (count($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 (count($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] + count($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] + count($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] + count($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 += count($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 (count($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 (count($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'] + count($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 + count($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 (!count($msg_ids))
0953      {
0954          return false;
0955      }
0956   
0957      switch ($mark_action)
0958      {
0959          case 'mark_important':
0960   
0961              if (!check_form_key('ucp_pm_view'))
0962              {
0963                  trigger_error('FORM_INVALID');
0964              }
0965   
0966              $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
0967                  SET pm_marked = 1 - pm_marked
0968                  WHERE folder_id = $cur_folder_id
0969                      AND user_id = $user_id
0970                      AND " . $db->sql_in_set('msg_id', $msg_ids);
0971              $db->sql_query($sql);
0972   
0973          break;
0974   
0975          case 'delete_marked':
0976   
0977              global $auth;
0978   
0979              if (!$auth->acl_get('u_pm_delete'))
0980              {
0981                  send_status_line(403, 'Forbidden');
0982                  trigger_error('NO_AUTH_DELETE_MESSAGE');
0983              }
0984   
0985              if (confirm_box(true))
0986              {
0987                  delete_pm($user_id, $msg_ids, $cur_folder_id);
0988   
0989                  $success_msg = (count($msg_ids) == 1) ? 'MESSAGE_DELETED' : 'MESSAGES_DELETED';
0990                  $redirect = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=' . $cur_folder_id);
0991   
0992                  meta_refresh(3, $redirect);
0993                  trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_FOLDER'], '<a href="' . $redirect . '">', '</a>'));
0994              }
0995              else
0996              {
0997                  $s_hidden_fields = array(
0998                      'cur_folder_id'    => $cur_folder_id,
0999                      'mark_option'    => 'delete_marked',
1000                      'submit_mark'    => true,
1001                      'marked_msg_id'    => $msg_ids
1002                  );
1003   
1004                  confirm_box(false, 'DELETE_MARKED_PM', build_hidden_fields($s_hidden_fields));
1005              }
1006   
1007          break;
1008   
1009          default:
1010              return false;
1011      }
1012   
1013      return true;
1014  }
1015   
1016  /**
1017  * Delete PM(s)
1018  */
1019  function delete_pm($user_id, $msg_ids, $folder_id)
1020  {
1021      global $db, $user, $phpbb_container, $phpbb_dispatcher;
1022   
1023      $user_id    = (int) $user_id;
1024      $folder_id    = (int) $folder_id;
1025   
1026      if (!$user_id)
1027      {
1028          return false;
1029      }
1030   
1031      if (!is_array($msg_ids))
1032      {
1033          if (!$msg_ids)
1034          {
1035              return false;
1036          }
1037          $msg_ids = array($msg_ids);
1038      }
1039   
1040      if (!count($msg_ids))
1041      {
1042          return false;
1043      }
1044   
1045      /**
1046      * Get all info for PM(s) before they are deleted
1047      *
1048      * @event core.delete_pm_before
1049      * @var    int    user_id     ID of the user requested the message delete
1050      * @var    array    msg_ids    array of all messages to be deleted
1051      * @var    int    folder_id    ID of the user folder where the messages are stored
1052      * @since 3.1.0-b5
1053      */
1054      $vars = array('user_id', 'msg_ids', 'folder_id');
1055      extract($phpbb_dispatcher->trigger_event('core.delete_pm_before', compact($vars)));
1056   
1057      // Get PM Information for later deleting
1058      $sql = 'SELECT msg_id, pm_unread, pm_new
1059          FROM ' . PRIVMSGS_TO_TABLE . '
1060          WHERE ' . $db->sql_in_set('msg_id', array_map('intval', $msg_ids)) . "
1061              AND folder_id = $folder_id
1062              AND user_id = $user_id";
1063      $result = $db->sql_query($sql);
1064   
1065      $delete_rows = array();
1066      $num_unread = $num_new = $num_deleted = 0;
1067      while ($row = $db->sql_fetchrow($result))
1068      {
1069          $num_unread += (int) $row['pm_unread'];
1070          $num_new += (int) $row['pm_new'];
1071   
1072          $delete_rows[$row['msg_id']] = 1;
1073      }
1074      $db->sql_freeresult($result);
1075      unset($msg_ids);
1076   
1077      if (!count($delete_rows))
1078      {
1079          return false;
1080      }
1081   
1082      $db->sql_transaction('begin');
1083   
1084      // if no one has read the message yet (meaning it is in users outbox)
1085      // then mark the message as deleted...
1086      if ($folder_id == PRIVMSGS_OUTBOX)
1087      {
1088          // Remove PM from Outbox
1089          $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . "
1090              WHERE user_id = $user_id AND folder_id = " . PRIVMSGS_OUTBOX . '
1091                  AND ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1092          $db->sql_query($sql);
1093   
1094          // Update PM Information for safety
1095          $sql = 'UPDATE ' . PRIVMSGS_TABLE . " SET message_text = ''
1096              WHERE " . $db->sql_in_set('msg_id', array_keys($delete_rows));
1097          $db->sql_query($sql);
1098   
1099          // Set delete flag for those intended to receive the PM
1100          // We do not remove the message actually, to retain some basic information (sent time for example)
1101          $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1102              SET pm_deleted = 1
1103              WHERE ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1104          $db->sql_query($sql);
1105   
1106          $num_deleted = $db->sql_affectedrows();
1107      }
1108      else
1109      {
1110          // Delete private message data
1111          $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . "
1112              WHERE user_id = $user_id
1113                  AND folder_id = $folder_id
1114                  AND " . $db->sql_in_set('msg_id', array_keys($delete_rows));
1115          $db->sql_query($sql);
1116          $num_deleted = $db->sql_affectedrows();
1117      }
1118   
1119      // if folder id is user defined folder then decrease pm_count
1120      if (!in_array($folder_id, array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX, PRIVMSGS_NO_BOX)))
1121      {
1122          $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
1123              SET pm_count = pm_count - $num_deleted
1124              WHERE folder_id = $folder_id";
1125          $db->sql_query($sql);
1126      }
1127   
1128      // Update unread and new status field
1129      if ($num_unread || $num_new)
1130      {
1131          $set_sql = ($num_unread) ? 'user_unread_privmsg = user_unread_privmsg - ' . $num_unread : '';
1132   
1133          if ($num_new)
1134          {
1135              $set_sql .= ($set_sql != '') ? ', ' : '';
1136              $set_sql .= 'user_new_privmsg = user_new_privmsg - ' . $num_new;
1137          }
1138   
1139          $db->sql_query('UPDATE ' . USERS_TABLE . " SET $set_sql WHERE user_id = $user_id");
1140   
1141          $user->data['user_new_privmsg'] -= $num_new;
1142          $user->data['user_unread_privmsg'] -= $num_unread;
1143      }
1144   
1145      /* @var $phpbb_notifications \phpbb\notification\manager */
1146      $phpbb_notifications = $phpbb_container->get('notification_manager');
1147   
1148      $phpbb_notifications->delete_notifications('notification.type.pm', array_keys($delete_rows));
1149   
1150      // Now we have to check which messages we can delete completely
1151      $sql = 'SELECT msg_id
1152          FROM ' . PRIVMSGS_TO_TABLE . '
1153          WHERE ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1154      $result = $db->sql_query($sql);
1155   
1156      while ($row = $db->sql_fetchrow($result))
1157      {
1158          unset($delete_rows[$row['msg_id']]);
1159      }
1160      $db->sql_freeresult($result);
1161   
1162      $delete_ids = array_keys($delete_rows);
1163   
1164      if (count($delete_ids))
1165      {
1166          // Check if there are any attachments we need to remove
1167          /** @var \phpbb\attachment\manager $attachment_manager */
1168          $attachment_manager = $phpbb_container->get('attachment.manager');
1169          $attachment_manager->delete('message', $delete_ids, false);
1170          unset($attachment_manager);
1171   
1172          $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1173              WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1174          $db->sql_query($sql);
1175      }
1176   
1177      $db->sql_transaction('commit');
1178   
1179      return true;
1180  }
1181   
1182  /**
1183  * Delete all PM(s) for given users and delete the ones without references
1184  *
1185  * @param    array        $user_ids    IDs of the users whose private messages we want to delete
1186  *
1187  * @return    boolean        False if there were no pms found, true otherwise.
1188  */
1189  function phpbb_delete_users_pms($user_ids)
1190  {
1191      global $db, $phpbb_container;
1192   
1193      $user_id_sql = $db->sql_in_set('user_id', $user_ids);
1194      $author_id_sql = $db->sql_in_set('author_id', $user_ids);
1195   
1196      // Get PM Information for later deleting
1197      // The two queries where split, so we can use our indexes
1198      $undelivered_msg = $delete_ids = array();
1199   
1200      // Part 1: get PMs the user received
1201      $sql = 'SELECT msg_id
1202          FROM ' . PRIVMSGS_TO_TABLE . '
1203          WHERE ' . $user_id_sql;
1204      $result = $db->sql_query($sql);
1205   
1206      while ($row = $db->sql_fetchrow($result))
1207      {
1208          $msg_id = (int) $row['msg_id'];
1209          $delete_ids[$msg_id] = $msg_id;
1210      }
1211      $db->sql_freeresult($result);
1212   
1213      // Part 2: get PMs the users sent, but are yet to be received.
1214      // We cannot simply delete them. First we have to check
1215      // whether another user already received and read the message.
1216      $sql = 'SELECT msg_id
1217          FROM ' . PRIVMSGS_TO_TABLE . '
1218          WHERE ' . $author_id_sql . '
1219              AND folder_id = ' . PRIVMSGS_NO_BOX;
1220      $result = $db->sql_query($sql);
1221   
1222      while ($row = $db->sql_fetchrow($result))
1223      {
1224          $msg_id = (int) $row['msg_id'];
1225          $undelivered_msg[$msg_id] = $msg_id;
1226      }
1227      $db->sql_freeresult($result);
1228   
1229      if (empty($delete_ids) && empty($undelivered_msg))
1230      {
1231          return false;
1232      }
1233   
1234      $db->sql_transaction('begin');
1235   
1236      /* @var $phpbb_notifications \phpbb\notification\manager */
1237      $phpbb_notifications = $phpbb_container->get('notification_manager');
1238   
1239      if (!empty($undelivered_msg))
1240      {
1241          // A pm is delivered, if for any recipient the message was moved
1242          // from their NO_BOX to another folder. We do not delete such
1243          // messages, but only delete them for users, who have not yet
1244          // received them.
1245          $sql = 'SELECT msg_id
1246              FROM ' . PRIVMSGS_TO_TABLE . '
1247              WHERE ' . $author_id_sql . '
1248                  AND folder_id <> ' . PRIVMSGS_NO_BOX . '
1249                  AND folder_id <> ' . PRIVMSGS_OUTBOX . '
1250                  AND folder_id <> ' . PRIVMSGS_SENTBOX;
1251          $result = $db->sql_query($sql);
1252   
1253          $delivered_msg = array();
1254          while ($row = $db->sql_fetchrow($result))
1255          {
1256              $msg_id = (int) $row['msg_id'];
1257              $delivered_msg[$msg_id] = $msg_id;
1258              unset($undelivered_msg[$msg_id]);
1259          }
1260          $db->sql_freeresult($result);
1261   
1262          $undelivered_user = array();
1263   
1264          // Count the messages we delete, so we can correct the user pm data
1265          $sql = 'SELECT user_id, COUNT(msg_id) as num_undelivered_privmsgs
1266              FROM ' . PRIVMSGS_TO_TABLE . '
1267              WHERE ' . $author_id_sql . '
1268                  AND folder_id = ' . PRIVMSGS_NO_BOX . '
1269                      AND ' . $db->sql_in_set('msg_id', array_merge($undelivered_msg, $delivered_msg)) . '
1270              GROUP BY user_id';
1271          $result = $db->sql_query($sql);
1272   
1273          while ($row = $db->sql_fetchrow($result))
1274          {
1275              $num_pms = (int) $row['num_undelivered_privmsgs'];
1276              $undelivered_user[$num_pms][] = (int) $row['user_id'];
1277   
1278              if (count($undelivered_user[$num_pms]) > 50)
1279              {
1280                  // If there are too many users affected the query might get
1281                  // too long, so we update the value for the first bunch here.
1282                  $sql = 'UPDATE ' . USERS_TABLE . '
1283                      SET user_new_privmsg = user_new_privmsg - ' . $num_pms . ',
1284                          user_unread_privmsg = user_unread_privmsg - ' . $num_pms . '
1285                      WHERE ' . $db->sql_in_set('user_id', $undelivered_user[$num_pms]);
1286                  $db->sql_query($sql);
1287                  unset($undelivered_user[$num_pms]);
1288              }
1289          }
1290          $db->sql_freeresult($result);
1291   
1292          foreach ($undelivered_user as $num_pms => $undelivered_user_set)
1293          {
1294              $sql = 'UPDATE ' . USERS_TABLE . '
1295                  SET user_new_privmsg = user_new_privmsg - ' . $num_pms . ',
1296                      user_unread_privmsg = user_unread_privmsg - ' . $num_pms . '
1297                  WHERE ' . $db->sql_in_set('user_id', $undelivered_user_set);
1298              $db->sql_query($sql);
1299          }
1300   
1301          if (!empty($delivered_msg))
1302          {
1303              $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
1304                  WHERE folder_id = ' . PRIVMSGS_NO_BOX . '
1305                      AND ' . $db->sql_in_set('msg_id', $delivered_msg);
1306              $db->sql_query($sql);
1307   
1308              $phpbb_notifications->delete_notifications('notification.type.pm', $delivered_msg);
1309          }
1310   
1311          if (!empty($undelivered_msg))
1312          {
1313              $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
1314                  WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
1315              $db->sql_query($sql);
1316   
1317              $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1318                  WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
1319              $db->sql_query($sql);
1320   
1321              $phpbb_notifications->delete_notifications('notification.type.pm', $undelivered_msg);
1322          }
1323      }
1324   
1325      // Reset the user's pm count to 0
1326      $sql = 'UPDATE ' . USERS_TABLE . '
1327          SET user_new_privmsg = 0,
1328              user_unread_privmsg = 0
1329          WHERE ' . $user_id_sql;
1330      $db->sql_query($sql);
1331   
1332      // Delete private message data of the user
1333      $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . '
1334          WHERE ' . $user_id_sql;
1335      $db->sql_query($sql);
1336   
1337      if (!empty($delete_ids))
1338      {
1339          // Now we have to check which messages we can delete completely
1340          $sql = 'SELECT msg_id
1341              FROM ' . PRIVMSGS_TO_TABLE . '
1342              WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1343          $result = $db->sql_query($sql);
1344   
1345          while ($row = $db->sql_fetchrow($result))
1346          {
1347              unset($delete_ids[$row['msg_id']]);
1348          }
1349          $db->sql_freeresult($result);
1350   
1351          if (!empty($delete_ids))
1352          {
1353              // Check if there are any attachments we need to remove
1354              /** @var \phpbb\attachment\manager $attachment_manager */
1355              $attachment_manager = $phpbb_container->get('attachment.manager');
1356              $attachment_manager->delete('message', $delete_ids, false);
1357              unset($attachment_manager);
1358   
1359              $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1360                  WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1361              $db->sql_query($sql);
1362   
1363              $phpbb_notifications->delete_notifications('notification.type.pm', $delete_ids);
1364          }
1365      }
1366   
1367      // Set the remaining author id to anonymous
1368      // This way users are still able to read messages from users being removed
1369      $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1370          SET author_id = ' . ANONYMOUS . '
1371          WHERE ' . $author_id_sql;
1372      $db->sql_query($sql);
1373   
1374      $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1375          SET author_id = ' . ANONYMOUS . '
1376          WHERE ' . $author_id_sql;
1377      $db->sql_query($sql);
1378   
1379      $db->sql_transaction('commit');
1380   
1381      return true;
1382  }
1383   
1384  /**
1385  * Rebuild message header
1386  */
1387  function rebuild_header($check_ary)
1388  {
1389      $address = array();
1390   
1391      foreach ($check_ary as $check_type => $address_field)
1392      {
1393          // Split Addresses into users and groups
1394          preg_match_all('/:?(u|g)_([0-9]+):?/', $address_field, $match);
1395   
1396          $u = $g = array();
1397          foreach ($match[1] as $id => $type)
1398          {
1399              ${$type}[] = (int) $match[2][$id];
1400          }
1401   
1402          $_types = array('u', 'g');
1403          foreach ($_types as $type)
1404          {
1405              if (count(${$type}))
1406              {
1407                  foreach (${$type} as $id)
1408                  {
1409                      $address[$type][$id] = $check_type;
1410                  }
1411              }
1412          }
1413      }
1414   
1415      return $address;
1416  }
1417   
1418  /**
1419  * Print out/assign recipient information
1420  */
1421  function write_pm_addresses($check_ary, $author_id, $plaintext = false)
1422  {
1423      global $db, $user, $template, $phpbb_root_path, $phpEx, $phpbb_container;
1424   
1425      /** @var \phpbb\group\helper $group_helper */
1426      $group_helper = $phpbb_container->get('group_helper');
1427   
1428      $addresses = array();
1429   
1430      foreach ($check_ary as $check_type => $address_field)
1431      {
1432          if (!is_array($address_field))
1433          {
1434              // Split Addresses into users and groups
1435              preg_match_all('/:?(u|g)_([0-9]+):?/', $address_field, $match);
1436   
1437              $u = $g = array();
1438              foreach ($match[1] as $id => $type)
1439              {
1440                  ${$type}[] = (int) $match[2][$id];
1441              }
1442          }
1443          else
1444          {
1445              $u = $address_field['u'];
1446              $g = $address_field['g'];
1447          }
1448   
1449          $address = array();
1450          if (count($u))
1451          {
1452              $sql = 'SELECT user_id, username, user_colour
1453                  FROM ' . USERS_TABLE . '
1454                  WHERE ' . $db->sql_in_set('user_id', $u);
1455              $result = $db->sql_query($sql);
1456   
1457              while ($row = $db->sql_fetchrow($result))
1458              {
1459                  if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1460                  {
1461                      if ($plaintext)
1462                      {
1463                          $address[] = $row['username'];
1464                      }
1465                      else
1466                      {
1467                          $address['user'][$row['user_id']] = array('name' => $row['username'], 'colour' => $row['user_colour']);
1468                      }
1469                  }
1470              }
1471              $db->sql_freeresult($result);
1472          }
1473   
1474          if (count($g))
1475          {
1476              if ($plaintext)
1477              {
1478                  $sql = 'SELECT group_name, group_type
1479                      FROM ' . GROUPS_TABLE . '
1480                          WHERE ' . $db->sql_in_set('group_id', $g);
1481                  $result = $db->sql_query($sql);
1482   
1483                  while ($row = $db->sql_fetchrow($result))
1484                  {
1485                      if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1486                      {
1487                          $address[] = $group_helper->get_name($row['group_name']);
1488                      }
1489                  }
1490                  $db->sql_freeresult($result);
1491              }
1492              else
1493              {
1494                  $sql = 'SELECT g.group_id, g.group_name, g.group_colour, g.group_type, ug.user_id
1495                      FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
1496                          WHERE ' . $db->sql_in_set('g.group_id', $g) . '
1497                          AND g.group_id = ug.group_id
1498                          AND ug.user_pending = 0';
1499                  $result = $db->sql_query($sql);
1500   
1501                  while ($row = $db->sql_fetchrow($result))
1502                  {
1503                      if (!isset($address['group'][$row['group_id']]))
1504                      {
1505                          if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1506                          {
1507                              $row['group_name'] = $group_helper->get_name($row['group_name']);
1508                              $address['group'][$row['group_id']] = array('name' => $row['group_name'], 'colour' => $row['group_colour']);
1509                          }
1510                      }
1511   
1512                      if (isset($address['user'][$row['user_id']]))
1513                      {
1514                          $address['user'][$row['user_id']]['in_group'] = $row['group_id'];
1515                      }
1516                  }
1517                  $db->sql_freeresult($result);
1518              }
1519          }
1520   
1521          if (count($address) && !$plaintext)
1522          {
1523              $template->assign_var('S_' . strtoupper($check_type) . '_RECIPIENT', true);
1524   
1525              foreach ($address as $type => $adr_ary)
1526              {
1527                  foreach ($adr_ary as $id => $row)
1528                  {
1529                      $tpl_ary = array(
1530                          'IS_GROUP'    => ($type == 'group') ? true : false,
1531                          'IS_USER'    => ($type == 'user') ? true : false,
1532                          'UG_ID'        => $id,
1533                          'NAME'        => $row['name'],
1534                          'COLOUR'    => ($row['colour']) ? '#' . $row['colour'] : '',
1535                          'TYPE'        => $type,
1536                      );
1537   
1538                      if ($type == 'user')
1539                      {
1540                          $tpl_ary = array_merge($tpl_ary, array(
1541                              'U_VIEW'        => get_username_string('profile', $id, $row['name'], $row['colour']),
1542                              'NAME_FULL'        => get_username_string('full', $id, $row['name'], $row['colour']),
1543                          ));
1544                      }
1545                      else
1546                      {
1547                          $tpl_ary = array_merge($tpl_ary, array(
1548                              'U_VIEW'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $id),
1549                          ));
1550                      }
1551   
1552                      $template->assign_block_vars($check_type . '_recipient', $tpl_ary);
1553                  }
1554              }
1555          }
1556   
1557          $addresses[$check_type] = $address;
1558      }
1559   
1560      return $addresses;
1561  }
1562   
1563  /**
1564  * Get folder status
1565  */
1566  function get_folder_status($folder_id, $folder)
1567  {
1568      global $user;
1569   
1570      if (isset($folder[$folder_id]))
1571      {
1572          $folder = $folder[$folder_id];
1573      }
1574      else
1575      {
1576          return false;
1577      }
1578   
1579      $return = array(
1580          'folder_name'    => $folder['folder_name'],
1581          'cur'            => $folder['num_messages'],
1582          'remaining'        => ($user->data['message_limit']) ? $user->data['message_limit'] - $folder['num_messages'] : 0,
1583          'max'            => $user->data['message_limit'],
1584          'percent'        => ($user->data['message_limit']) ? (($user->data['message_limit'] > 0) ? floor(($folder['num_messages'] / $user->data['message_limit']) * 100) : 100) : 0,
1585      );
1586   
1587      $return['message']    = $user->lang('FOLDER_STATUS_MSG', $user->lang('MESSAGES_COUNT', (int) $return['max']), (int) $return['cur'], $return['percent']);
1588   
1589      return $return;
1590  }
1591   
1592  //
1593  // COMPOSE MESSAGES
1594  //
1595   
1596  /**
1597  * Submit PM
1598  */
1599  function submit_pm($mode, $subject, &$data_ary, $put_in_outbox = true)
1600  {
1601      global $db, $auth, $config, $user, $phpbb_root_path, $phpbb_container, $phpbb_dispatcher, $request;
1602   
1603      // We do not handle erasing pms here
1604      if ($mode == 'delete')
1605      {
1606          return false;
1607      }
1608   
1609      $current_time = time();
1610   
1611      $data = $data_ary;
1612      /**
1613      * Get all parts of the PM that are to be submited to the DB.
1614      *
1615      * @event core.submit_pm_before
1616      * @var    string    mode    PM Post mode - post|reply|quote|quotepost|forward|edit
1617      * @var    string    subject    Subject of the private message
1618      * @var    array    data    The whole row data of the PM.
1619      * @since 3.1.0-b3
1620      */
1621      $vars = array('mode', 'subject', 'data');
1622      extract($phpbb_dispatcher->trigger_event('core.submit_pm_before', compact($vars)));
1623      $data_ary = $data;
1624      unset($data);
1625   
1626      // Collect some basic information about which tables and which rows to update/insert
1627      $sql_data = array();
1628      $root_level = 0;
1629   
1630      // Recipient Information
1631      $recipients = $to = $bcc = array();
1632   
1633      if ($mode != 'edit')
1634      {
1635          // Build Recipient List
1636          // u|g => array($user_id => 'to'|'bcc')
1637          $_types = array('u', 'g');
1638          foreach ($_types as $ug_type)
1639          {
1640              if (isset($data_ary['address_list'][$ug_type]) && count($data_ary['address_list'][$ug_type]))
1641              {
1642                  foreach ($data_ary['address_list'][$ug_type] as $id => $field)
1643                  {
1644                      $id = (int) $id;
1645   
1646                      // Do not rely on the address list being "valid"
1647                      if (!$id || ($ug_type == 'u' && $id == ANONYMOUS))
1648                      {
1649                          continue;
1650                      }
1651   
1652                      $field = ($field == 'to') ? 'to' : 'bcc';
1653                      if ($ug_type == 'u')
1654                      {
1655                          $recipients[$id] = $field;
1656                      }
1657                      ${$field}[] = $ug_type . '_' . $id;
1658                  }
1659              }
1660          }
1661   
1662          if (isset($data_ary['address_list']['g']) && count($data_ary['address_list']['g']))
1663          {
1664              // We need to check the PM status of group members (do they want to receive PM's?)
1665              // Only check if not a moderator or admin, since they are allowed to override this user setting
1666              $sql_allow_pm = (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) ? ' AND u.user_allow_pm = 1' : '';
1667   
1668              $sql = 'SELECT u.user_type, ug.group_id, ug.user_id
1669                  FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
1670                  WHERE ' . $db->sql_in_set('ug.group_id', array_keys($data_ary['address_list']['g'])) . '
1671                      AND ug.user_pending = 0
1672                      AND u.user_id = ug.user_id
1673                      AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')' .
1674                      $sql_allow_pm;
1675              $result = $db->sql_query($sql);
1676   
1677              while ($row = $db->sql_fetchrow($result))
1678              {
1679                  $field = ($data_ary['address_list']['g'][$row['group_id']] == 'to') ? 'to' : 'bcc';
1680                  $recipients[$row['user_id']] = $field;
1681              }
1682              $db->sql_freeresult($result);
1683          }
1684   
1685          if (!count($recipients))
1686          {
1687              trigger_error('NO_RECIPIENT');
1688          }
1689      }
1690   
1691      // First of all make sure the subject are having the correct length.
1692      $subject = truncate_string($subject, $mode === 'post' ? 120 : 124);
1693   
1694      $db->sql_transaction('begin');
1695   
1696      $sql = '';
1697   
1698      switch ($mode)
1699      {
1700          case 'reply':
1701          case 'quote':
1702              $root_level = ($data_ary['reply_from_root_level']) ? $data_ary['reply_from_root_level'] : $data_ary['reply_from_msg_id'];
1703   
1704              // Set message_replied switch for this user
1705              $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1706                  SET pm_replied = 1
1707                  WHERE user_id = ' . $data_ary['from_user_id'] . '
1708                      AND msg_id = ' . $data_ary['reply_from_msg_id'];
1709   
1710          // no break
1711   
1712          case 'forward':
1713          case 'post':
1714          case 'quotepost':
1715              $sql_data = array(
1716                  'root_level'        => $root_level,
1717                  'author_id'            => $data_ary['from_user_id'],
1718                  'icon_id'            => $data_ary['icon_id'],
1719                  'author_ip'            => $data_ary['from_user_ip'],
1720                  'message_time'        => $current_time,
1721                  'enable_bbcode'        => $data_ary['enable_bbcode'],
1722                  'enable_smilies'    => $data_ary['enable_smilies'],
1723                  'enable_magic_url'    => $data_ary['enable_urls'],
1724                  'enable_sig'        => $data_ary['enable_sig'],
1725                  'message_subject'    => $subject,
1726                  'message_text'        => $data_ary['message'],
1727                  'message_attachment'=> (!empty($data_ary['attachment_data'])) ? 1 : 0,
1728                  'bbcode_bitfield'    => $data_ary['bbcode_bitfield'],
1729                  'bbcode_uid'        => $data_ary['bbcode_uid'],
1730                  'to_address'        => implode(':', $to),
1731                  'bcc_address'        => implode(':', $bcc),
1732                  'message_reported'    => 0,
1733              );
1734          break;
1735   
1736          case 'edit':
1737              $sql_data = array(
1738                  'icon_id'            => $data_ary['icon_id'],
1739                  'message_edit_time'    => $current_time,
1740                  'enable_bbcode'        => $data_ary['enable_bbcode'],
1741                  'enable_smilies'    => $data_ary['enable_smilies'],
1742                  'enable_magic_url'    => $data_ary['enable_urls'],
1743                  'enable_sig'        => $data_ary['enable_sig'],
1744                  'message_subject'    => $subject,
1745                  'message_text'        => $data_ary['message'],
1746                  'message_attachment'=> (!empty($data_ary['attachment_data'])) ? 1 : 0,
1747                  'bbcode_bitfield'    => $data_ary['bbcode_bitfield'],
1748                  'bbcode_uid'        => $data_ary['bbcode_uid']
1749              );
1750          break;
1751      }
1752   
1753      if (count($sql_data))
1754      {
1755          if ($mode == 'post' || $mode == 'reply' || $mode == 'quote' || $mode == 'quotepost' || $mode == 'forward')
1756          {
1757              $db->sql_query('INSERT INTO ' . PRIVMSGS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_data));
1758              $data_ary['msg_id'] = $db->sql_nextid();
1759          }
1760          else if ($mode == 'edit')
1761          {
1762              $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1763                  SET message_edit_count = message_edit_count + 1, ' . $db->sql_build_array('UPDATE', $sql_data) . '
1764                  WHERE msg_id = ' . $data_ary['msg_id'];
1765              $db->sql_query($sql);
1766          }
1767      }
1768   
1769      if ($mode != 'edit')
1770      {
1771          if ($sql)
1772          {
1773              $db->sql_query($sql);
1774          }
1775          unset($sql);
1776   
1777          $sql_ary = array();
1778          foreach ($recipients as $user_id => $type)
1779          {
1780              $sql_ary[] = array(
1781                  'msg_id'        => (int) $data_ary['msg_id'],
1782                  'user_id'        => (int) $user_id,
1783                  'author_id'        => (int) $data_ary['from_user_id'],
1784                  'folder_id'        => PRIVMSGS_NO_BOX,
1785                  'pm_new'        => 1,
1786                  'pm_unread'        => 1,
1787                  'pm_forwarded'    => ($mode == 'forward') ? 1 : 0
1788              );
1789          }
1790   
1791          $db->sql_multi_insert(PRIVMSGS_TO_TABLE, $sql_ary);
1792   
1793          $sql = 'UPDATE ' . USERS_TABLE . '
1794              SET user_new_privmsg = user_new_privmsg + 1, user_unread_privmsg = user_unread_privmsg + 1, user_last_privmsg = ' . time() . '
1795              WHERE ' . $db->sql_in_set('user_id', array_keys($recipients));
1796          $db->sql_query($sql);
1797   
1798          // Put PM into outbox
1799          if ($put_in_outbox)
1800          {
1801              $db->sql_query('INSERT INTO ' . PRIVMSGS_TO_TABLE . ' ' . $db->sql_build_array('INSERT', array(
1802                  'msg_id'        => (int) $data_ary['msg_id'],
1803                  'user_id'        => (int) $data_ary['from_user_id'],
1804                  'author_id'        => (int) $data_ary['from_user_id'],
1805                  'folder_id'        => PRIVMSGS_OUTBOX,
1806                  'pm_new'        => 0,
1807                  'pm_unread'        => 0,
1808                  'pm_forwarded'    => ($mode == 'forward') ? 1 : 0))
1809              );
1810          }
1811      }
1812   
1813      // Set user last post time
1814      if ($mode == 'reply' || $mode == 'quote' || $mode == 'quotepost' || $mode == 'forward' || $mode == 'post')
1815      {
1816          $sql = 'UPDATE ' . USERS_TABLE . "
1817              SET user_lastpost_time = $current_time
1818              WHERE user_id = " . $data_ary['from_user_id'];
1819          $db->sql_query($sql);
1820      }
1821   
1822      // Submit Attachments
1823      if (!empty($data_ary['attachment_data']) && $data_ary['msg_id'] && in_array($mode, array('post', 'reply', 'quote', 'quotepost', 'edit', 'forward')))
1824      {
1825          $space_taken = $files_added = 0;
1826          $orphan_rows = array();
1827   
1828          foreach ($data_ary['attachment_data'] as $pos => $attach_row)
1829          {
1830              $orphan_rows[(int) $attach_row['attach_id']] = array();
1831          }
1832   
1833          if (count($orphan_rows))
1834          {
1835              $sql = 'SELECT attach_id, filesize, physical_filename
1836                  FROM ' . ATTACHMENTS_TABLE . '
1837                  WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan_rows)) . '
1838                      AND in_message = 1
1839                      AND is_orphan = 1
1840                      AND poster_id = ' . $user->data['user_id'];
1841              $result = $db->sql_query($sql);
1842   
1843              $orphan_rows = array();
1844              while ($row = $db->sql_fetchrow($result))
1845              {
1846                  $orphan_rows[$row['attach_id']] = $row;
1847              }
1848              $db->sql_freeresult($result);
1849          }
1850   
1851          foreach ($data_ary['attachment_data'] as $pos => $attach_row)
1852          {
1853              if ($attach_row['is_orphan'] && !isset($orphan_rows[$attach_row['attach_id']]))
1854              {
1855                  continue;
1856              }
1857   
1858              if (!$attach_row['is_orphan'])
1859              {
1860                  // update entry in db if attachment already stored in db and filespace
1861                  $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
1862                      SET attach_comment = '" . $db->sql_escape($attach_row['attach_comment']) . "'
1863                      WHERE attach_id = " . (int) $attach_row['attach_id'] . '
1864                          AND is_orphan = 0';
1865                  $db->sql_query($sql);
1866              }
1867              else
1868              {
1869                  // insert attachment into db
1870                  if (!@file_exists($phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($orphan_rows[$attach_row['attach_id']]['physical_filename'])))
1871                  {
1872                      continue;
1873                  }
1874   
1875                  $space_taken += $orphan_rows[$attach_row['attach_id']]['filesize'];
1876                  $files_added++;
1877   
1878                  $attach_sql = array(
1879                      'post_msg_id'        => $data_ary['msg_id'],
1880                      'topic_id'            => 0,
1881                      'is_orphan'            => 0,
1882                      'poster_id'            => $data_ary['from_user_id'],
1883                      'attach_comment'    => $attach_row['attach_comment'],
1884                  );
1885   
1886                  $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $attach_sql) . '
1887                      WHERE attach_id = ' . $attach_row['attach_id'] . '
1888                          AND is_orphan = 1
1889                          AND poster_id = ' . $user->data['user_id'];
1890                  $db->sql_query($sql);
1891              }
1892          }
1893   
1894          if ($space_taken && $files_added)
1895          {
1896              $config->increment('upload_dir_size', $space_taken, false);
1897              $config->increment('num_files', $files_added, false);
1898          }
1899      }
1900   
1901      // Delete draft if post was loaded...
1902      $draft_id = $request->variable('draft_loaded', 0);
1903      if ($draft_id)
1904      {
1905          $sql = 'DELETE FROM ' . DRAFTS_TABLE . "
1906              WHERE draft_id = $draft_id
1907                  AND user_id = " . $data_ary['from_user_id'];
1908          $db->sql_query($sql);
1909      }
1910   
1911      $db->sql_transaction('commit');
1912   
1913      // Send Notifications
1914      $pm_data = array_merge($data_ary, array(
1915          'message_subject'        => $subject,
1916          'recipients'            => $recipients,
1917      ));
1918   
1919      /* @var $phpbb_notifications \phpbb\notification\manager */
1920      $phpbb_notifications = $phpbb_container->get('notification_manager');
1921   
1922      if ($mode == 'edit')
1923      {
1924          $phpbb_notifications->update_notifications('notification.type.pm', $pm_data);
1925      }
1926      else
1927      {
1928          $phpbb_notifications->add_notifications('notification.type.pm', $pm_data);
1929      }
1930   
1931      $data = $data_ary;
1932      /**
1933      * Get PM message ID after submission to DB
1934      *
1935      * @event core.submit_pm_after
1936      * @var    string    mode    PM Post mode - post|reply|quote|quotepost|forward|edit
1937      * @var    string    subject    Subject of the private message
1938      * @var    array    data    The whole row data of the PM.
1939      * @var    array    pm_data    The data sent to notification class
1940      * @since 3.1.0-b5
1941      */
1942      $vars = array('mode', 'subject', 'data', 'pm_data');
1943      extract($phpbb_dispatcher->trigger_event('core.submit_pm_after', compact($vars)));
1944      $data_ary = $data;
1945      unset($data);
1946   
1947      return $data_ary['msg_id'];
1948  }
1949   
1950  /**
1951  * Display Message History
1952  */
1953  function message_history($msg_id, $user_id, $message_row, $folder, $in_post_mode = false)
1954  {
1955      global $db, $user, $template, $phpbb_root_path, $phpEx, $auth, $phpbb_dispatcher;
1956   
1957      // Select all receipts and the author from the pm we currently view, to only display their pm-history
1958      $sql = 'SELECT author_id, user_id
1959          FROM ' . PRIVMSGS_TO_TABLE . "
1960          WHERE msg_id = $msg_id
1961              AND folder_id <> " . PRIVMSGS_HOLD_BOX;
1962      $result = $db->sql_query($sql);
1963   
1964      $recipients = array();
1965      while ($row = $db->sql_fetchrow($result))
1966      {
1967          $recipients[] = (int) $row['user_id'];
1968          $recipients[] = (int) $row['author_id'];
1969      }
1970      $db->sql_freeresult($result);
1971      $recipients = array_unique($recipients);
1972   
1973      // Get History Messages (could be newer)
1974      $sql_where = 't.msg_id = p.msg_id
1975              AND p.author_id = u.user_id
1976              AND t.folder_id NOT IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ')
1977              AND ' . $db->sql_in_set('t.author_id', $recipients, false, true) . "
1978              AND t.user_id = $user_id";
1979   
1980      // We no longer need those.
1981      unset($recipients);
1982   
1983      if (!$message_row['root_level'])
1984      {
1985          $sql_where .= " AND (p.root_level = $msg_id OR (p.root_level = 0 AND p.msg_id = $msg_id))";
1986      }
1987      else
1988      {
1989          $sql_where .= " AND (p.root_level = " . $message_row['root_level'] . ' OR p.msg_id = ' . $message_row['root_level'] . ')';
1990      }
1991   
1992      $sql_ary = array(
1993          'SELECT'    => 't.*, p.*, u.*',
1994          'FROM'        => array(
1995              PRIVMSGS_TABLE        => 'p',
1996              PRIVMSGS_TO_TABLE    => 't',
1997              USERS_TABLE            => 'u'
1998          ),
1999          'LEFT_JOIN'    => array(),
2000          'WHERE'        => $sql_where,
2001          'ORDER_BY'    => 'p.message_time DESC',
2002      );
2003   
2004      /**
2005      * Event to modify the SQL query before the message history in private message is queried
2006      *
2007      * @event core.message_history_modify_sql_ary
2008      * @var    array    sql_ary        The SQL array to get the data of the message history in private message
2009      * @since 3.2.8-RC1
2010      */
2011      $vars = array('sql_ary');
2012      extract($phpbb_dispatcher->trigger_event('core.message_history_modify_sql_ary', compact($vars)));
2013   
2014      $sql = $db->sql_build_query('SELECT', $sql_ary);
2015      unset($sql_ary);
2016   
2017      $result = $db->sql_query($sql);
2018      $row = $db->sql_fetchrow($result);
2019   
2020      if (!$row)
2021      {
2022          $db->sql_freeresult($result);
2023          return false;
2024      }
2025   
2026      $title = $row['message_subject'];
2027   
2028      $rowset = array();
2029      $folder_url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm') . '&amp;folder=';
2030   
2031      do
2032      {
2033          $folder_id = (int) $row['folder_id'];
2034   
2035          $row['folder'][] = (isset($folder[$folder_id])) ? '<a href="' . $folder_url . $folder_id . '">' . $folder[$folder_id]['folder_name'] . '</a>' : $user->lang['UNKNOWN_FOLDER'];
2036   
2037          if (isset($rowset[$row['msg_id']]))
2038          {
2039              $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'];
2040          }
2041          else
2042          {
2043              $rowset[$row['msg_id']] = $row;
2044          }
2045      }
2046      while ($row = $db->sql_fetchrow($result));
2047      $db->sql_freeresult($result);
2048   
2049      $url = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm');
2050   
2051      /**
2052      * Modify message rows before displaying the history in private messages
2053      *
2054      * @event core.message_history_modify_rowset
2055      * @var int        msg_id            ID of the private message
2056      * @var int        user_id            ID of the message author
2057      * @var array    message_row        Array with message data
2058      * @var array    folder            Array with data of user's message folders
2059      * @var bool        in_post_mode    Whether or not we are viewing or composing
2060      * @var array    rowset            Array with message history data
2061      * @var string    url                Base URL used to generate links to private messages
2062      * @var string    title            Subject of the private message
2063      * @since 3.2.10-RC1
2064      * @since 3.3.1-RC1
2065      */
2066      $vars = [
2067          'msg_id',
2068          'user_id',
2069          'message_row',
2070          'folder',
2071          'in_post_mode',
2072          'rowset',
2073          'url',
2074          'title',
2075      ];
2076      extract($phpbb_dispatcher->trigger_event('core.message_history_modify_rowset', compact($vars)));
2077   
2078      if (count($rowset) == 1 && !$in_post_mode)
2079      {
2080          return false;
2081      }
2082   
2083      $title = censor_text($title);
2084   
2085      $next_history_pm = $previous_history_pm = $prev_id = 0;
2086   
2087      // Re-order rowset to be able to get the next/prev message rows...
2088      $rowset = array_values($rowset);
2089   
2090      for ($i = 0, $size = count($rowset); $i < $size; $i++)
2091      {
2092          $row = &$rowset[$i];
2093          $id = (int) $row['msg_id'];
2094   
2095          $author_id    = $row['author_id'];
2096          $folder_id    = (int) $row['folder_id'];
2097   
2098          $subject    = $row['message_subject'];
2099          $message    = $row['message_text'];
2100   
2101          $message = censor_text($message);
2102   
2103          $decoded_message = false;
2104   
2105          if ($in_post_mode && $auth->acl_get('u_sendpm') && $author_id != ANONYMOUS)
2106          {
2107              $decoded_message = $message;
2108              decode_message($decoded_message, $row['bbcode_uid']);
2109   
2110              $decoded_message = bbcode_nl2br($decoded_message);
2111          }
2112   
2113          $parse_flags = ($row['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0);
2114          $parse_flags |= ($row['enable_smilies'] ? OPTION_FLAG_SMILIES : 0);
2115   
2116          $message = generate_text_for_display($message, $row['bbcode_uid'], $row['bbcode_bitfield'], $parse_flags, false);
2117   
2118          $subject = censor_text($subject);
2119   
2120          if ($id == $msg_id)
2121          {
2122              $next_history_pm = (isset($rowset[$i + 1])) ? (int) $rowset[$i + 1]['msg_id'] : 0;
2123              $previous_history_pm = $prev_id;
2124          }
2125   
2126          $template_vars = array(
2127              'MESSAGE_AUTHOR_QUOTE'        => (($decoded_message) ? addslashes(get_username_string('username', $author_id, $row['username'], $row['user_colour'], $row['username'])) : ''),
2128              'MESSAGE_AUTHOR_FULL'        => get_username_string('full', $author_id, $row['username'], $row['user_colour'], $row['username']),
2129              'MESSAGE_AUTHOR_COLOUR'        => get_username_string('colour', $author_id, $row['username'], $row['user_colour'], $row['username']),
2130              'MESSAGE_AUTHOR'            => get_username_string('username', $author_id, $row['username'], $row['user_colour'], $row['username']),
2131              'U_MESSAGE_AUTHOR'            => get_username_string('profile', $author_id, $row['username'], $row['user_colour'], $row['username']),
2132   
2133              'SUBJECT'            => $subject,
2134              'SENT_DATE'            => $user->format_date($row['message_time']),
2135              'MESSAGE'            => $message,
2136              'FOLDER'            => implode($user->lang['COMMA_SEPARATOR'], $row['folder']),
2137              'DECODED_MESSAGE'    => $decoded_message,
2138   
2139              'S_CURRENT_MSG'        => ($row['msg_id'] == $msg_id),
2140              'S_AUTHOR_DELETED'    => ($author_id == ANONYMOUS) ? true : false,
2141              'S_IN_POST_MODE'    => $in_post_mode,
2142   
2143              'MSG_ID'            => $row['msg_id'],
2144              'MESSAGE_TIME'        => $row['message_time'],
2145              'USER_ID'            => $row['user_id'],
2146              'U_VIEW_MESSAGE'    => "$url&amp;f=$folder_id&amp;p=" . $row['msg_id'],
2147              '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'] : '',
2148              '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'] : ''
2149          );
2150   
2151          /**
2152          * Modify the template vars for displaying the message history in private message
2153          *
2154          * @event core.message_history_modify_template_vars
2155          * @var array    template_vars        Array containing the query
2156          * @var array    row                    Array containing the action user row
2157          * @since 3.2.8-RC1
2158          */
2159          $vars = array(
2160              'template_vars',
2161              'row',
2162          );
2163          extract($phpbb_dispatcher->trigger_event('core.message_history_modify_template_vars', compact($vars)));
2164   
2165          $template->assign_block_vars('history_row', $template_vars);
2166   
2167          unset($rowset[$i]);
2168          $prev_id = $id;
2169      }
2170   
2171      $template->assign_vars(array(
2172          'QUOTE_IMG'            => $user->img('icon_post_quote', $user->lang['REPLY_WITH_QUOTE']),
2173          'HISTORY_TITLE'        => $title,
2174   
2175          'U_VIEW_NEXT_HISTORY'        => ($next_history_pm) ? "$url&amp;p=" . $next_history_pm : '',
2176          'U_VIEW_PREVIOUS_HISTORY'    => ($previous_history_pm) ? "$url&amp;p=" . $previous_history_pm : '',
2177      ));
2178   
2179      return true;
2180  }
2181   
2182  /**
2183  * Set correct users max messages in PM folder.
2184  * If several group memberships define different amount of messages, the highest will be chosen.
2185  */
2186  function set_user_message_limit()
2187  {
2188      global $user, $db, $config;
2189   
2190      // Get maximum about from user memberships
2191      $message_limit = phpbb_get_max_setting_from_group($db, $user->data['user_id'], 'message_limit');
2192   
2193      // If it is 0, there is no limit set and we use the maximum value within the config.
2194      $user->data['message_limit'] = (!$message_limit) ? $config['pm_max_msgs'] : $message_limit;
2195  }
2196   
2197  /**
2198   * Get the maximum PM setting for the groups of the user
2199   *
2200   * @param \phpbb\db\driver\driver_interface $db
2201   * @param int $user_id
2202   * @param string $setting Only 'max_recipients' and 'message_limit' are supported
2203   * @return int The maximum setting for all groups of the user, unless one group has '0'
2204   * @throws \InvalidArgumentException If selected group setting is not supported
2205   */
2206  function phpbb_get_max_setting_from_group(\phpbb\db\driver\driver_interface $db, $user_id, $setting)
2207  {
2208      if ($setting !== 'max_recipients' && $setting !== 'message_limit')
2209      {
2210          throw new InvalidArgumentException('Setting "' . $setting . '" is not supported');
2211      }
2212   
2213      // Get maximum number of allowed recipients
2214      $sql = 'SELECT MAX(g.group_' . $setting . ') as max_setting
2215          FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
2216          WHERE ug.user_id = ' . (int) $user_id . '
2217              AND ug.user_pending = 0
2218              AND ug.group_id = g.group_id';
2219      $result = $db->sql_query($sql);
2220      $row = $db->sql_fetchrow($result);
2221      $db->sql_freeresult($result);
2222      $max_setting = (int) $row['max_setting'];
2223   
2224      return $max_setting;
2225  }
2226   
2227  /**
2228  * Generates an array of coloured recipient names from a list of PMs - (groups & users)
2229  *
2230  * @param    array    $pm_by_id    An array of rows from PRIVMSGS_TABLE, keys are the msg_ids.
2231  *
2232  * @return    array                2D Array: array(msg_id => array('username or group string', ...), ...)
2233  *                                Usernames are generated with {@link get_username_string get_username_string}
2234  *                                Groups are coloured and have a link to the membership page
2235  */
2236  function get_recipient_strings($pm_by_id)
2237  {
2238      global $db, $phpbb_root_path, $phpEx, $user, $phpbb_container;
2239   
2240      /** @var \phpbb\group\helper $group_helper */
2241      $group_helper = $phpbb_container->get('group_helper');
2242   
2243      $address_list = $recipient_list = $address = array();
2244   
2245      $_types = array('u', 'g');
2246   
2247      foreach ($pm_by_id as $message_id => $row)
2248      {
2249          $address[$message_id] = rebuild_header(array('to' => $row['to_address'], 'bcc' => $row['bcc_address']));
2250   
2251          foreach ($_types as $ug_type)
2252          {
2253              if (isset($address[$message_id][$ug_type]) && count($address[$message_id][$ug_type]))
2254              {
2255                  foreach ($address[$message_id][$ug_type] as $ug_id => $in_to)
2256                  {
2257                      $recipient_list[$ug_type][$ug_id] = array('name' => $user->lang['NA'], 'colour' => '');
2258                  }
2259              }
2260          }
2261      }
2262   
2263      foreach ($_types as $ug_type)
2264      {
2265          if (!empty($recipient_list[$ug_type]))
2266          {
2267              if ($ug_type == 'u')
2268              {
2269                  $sql = 'SELECT user_id as id, username as name, user_colour as colour
2270                      FROM ' . USERS_TABLE . '
2271                      WHERE ';
2272              }
2273              else
2274              {
2275                  $sql = 'SELECT group_id as id, group_name as name, group_colour as colour, group_type
2276                      FROM ' . GROUPS_TABLE . '
2277                      WHERE ';
2278              }
2279              $sql .= $db->sql_in_set(($ug_type == 'u') ? 'user_id' : 'group_id', array_map('intval', array_keys($recipient_list[$ug_type])));
2280   
2281              $result = $db->sql_query($sql);
2282   
2283              while ($row = $db->sql_fetchrow($result))
2284              {
2285                  if ($ug_type == 'g')
2286                  {
2287                      $row['name'] = $group_helper->get_name($row['name']);
2288                  }
2289   
2290                  $recipient_list[$ug_type][$row['id']] = array('name' => $row['name'], 'colour' => $row['colour']);
2291              }
2292              $db->sql_freeresult($result);
2293          }
2294      }
2295   
2296      foreach ($address as $message_id => $adr_ary)
2297      {
2298          foreach ($adr_ary as $type => $id_ary)
2299          {
2300              foreach ($id_ary as $ug_id => $_id)
2301              {
2302                  if ($type == 'u')
2303                  {
2304                      $address_list[$message_id][] = get_username_string('full', $ug_id, $recipient_list[$type][$ug_id]['name'], $recipient_list[$type][$ug_id]['colour']);
2305                  }
2306                  else
2307                  {
2308                      $user_colour = ($recipient_list[$type][$ug_id]['colour']) ? ' style="font-weight: bold; color:#' . $recipient_list[$type][$ug_id]['colour'] . '"' : '';
2309                      $link = '<a href="' . append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group&amp;g=' . $ug_id) . '"' . $user_colour . '>';
2310                      $address_list[$message_id][] = $link . $recipient_list[$type][$ug_id]['name'] . (($link) ? '</a>' : '');
2311                  }
2312              }
2313          }
2314      }
2315   
2316      return $address_list;
2317  }
2318