phpBB
Statistics
| Revision:

root / branches / phpBB-3_0_0 / phpBB / includes / functions_admin.php

History | View | Annotate | Download (88.5 kB)

1
<?php
2
/**
3
*
4
* @package acp
5
* @version $Id: functions_admin.php 11612 2011-12-04 04:15:41Z git-gate $
6
* @copyright (c) 2005 phpBB Group
7
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8
*
9
*/
10
11
/**
12
* @ignore
13
*/
14
if (!defined('IN_PHPBB'))
15
{
16
        exit;
17
}
18
19
/**
20
* Recalculate Nested Sets
21
*
22
* @param int        $new_id        first left_id (should start with 1)
23
* @param string        $pkey        primary key-column (containing the id for the parent_id of the children)
24
* @param string        $table        constant or fullname of the table
25
* @param int        $parent_id parent_id of the current set (default = 0)
26
* @param array        $where        contains strings to compare closer on the where statement (additional)
27
*
28
* @author EXreaction
29
*/
30
function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array())
31
{
32
        global $db;
33
34
        $sql = 'SELECT *
35
                FROM ' . $table . '
36
                WHERE parent_id = ' . (int) $parent_id .
37
                ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . '
38
                ORDER BY left_id ASC';
39
        $result = $db->sql_query($sql);
40
        while ($row = $db->sql_fetchrow($result))
41
        {
42
                // First we update the left_id for this module
43
                if ($row['left_id'] != $new_id)
44
                {
45
                        $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
46
                }
47
                $new_id++;
48
49
                // Then we go through any children and update their left/right id's
50
                recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where);
51
52
                // Then we come back and update the right_id for this module
53
                if ($row['right_id'] != $new_id)
54
                {
55
                        $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
56
                }
57
                $new_id++;
58
        }
59
        $db->sql_freeresult($result);
60
}
61
62
/**
63
* Simple version of jumpbox, just lists authed forums
64
*/
65
function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = false, $ignore_nonpost = false, $ignore_emptycat = true, $only_acl_post = false, $return_array = false)
66
{
67
        global $db, $user, $auth;
68
69
        // This query is identical to the jumpbox one
70
        $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id
71
                FROM ' . FORUMS_TABLE . '
72
                ORDER BY left_id ASC';
73
        $result = $db->sql_query($sql, 600);
74
75
        $right = 0;
76
        $padding_store = array('0' => '');
77
        $padding = '';
78
        $forum_list = ($return_array) ? array() : '';
79
80
        // Sometimes it could happen that forums will be displayed here not be displayed within the index page
81
        // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
82
        // If this happens, the padding could be "broken"
83
84
        while ($row = $db->sql_fetchrow($result))
85
        {
86
                if ($row['left_id'] < $right)
87
                {
88
                        $padding .= '&nbsp; &nbsp;';
89
                        $padding_store[$row['parent_id']] = $padding;
90
                }
91
                else if ($row['left_id'] > $right + 1)
92
                {
93
                        $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
94
                }
95
96
                $right = $row['right_id'];
97
                $disabled = false;
98
99
                if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id']))
100
                {
101
                        if ($only_acl_post && !$auth->acl_get('f_post', $row['forum_id']) || (!$auth->acl_get('m_approve', $row['forum_id']) && !$auth->acl_get('f_noapprove', $row['forum_id'])))
102
                        {
103
                                $disabled = true;
104
                        }
105
                }
106
                else if (!$ignore_acl)
107
                {
108
                        continue;
109
                }
110
111
                if (
112
                        ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id)
113
                        ||
114
                        // Non-postable forum with no subforums, don't display
115
                        ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat)
116
                        ||
117
                        ($row['forum_type'] != FORUM_POST && $ignore_nonpost)
118
                        )
119
                {
120
                        $disabled = true;
121
                }
122
123
                if ($return_array)
124
                {
125
                        // Include some more information...
126
                        $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false);
127
                        $forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
128
                }
129
                else
130
                {
131
                        $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : '');
132
                        $forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';
133
                }
134
        }
135
        $db->sql_freeresult($result);
136
        unset($padding_store);
137
138
        return $forum_list;
139
}
140
141
/**
142
* Generate size select options
143
*/
144
function size_select_options($size_compare)
145
{
146
        global $user;
147
148
        $size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']);
149
        $size_types = array('b', 'kb', 'mb');
150
151
        $s_size_options = '';
152
153
        for ($i = 0, $size = sizeof($size_types_text); $i < $size; $i++)
154
        {
155
                $selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : '';
156
                $s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>';
157
        }
158
159
        return $s_size_options;
160
}
161
162
/**
163
* Generate list of groups (option fields without select)
164
*
165
* @param int $group_id The default group id to mark as selected
166
* @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id
167
* @param int $manage_founder If set to false (default) all groups are returned, if 0 only those groups returned not being managed by founders only, if 1 only those groups returned managed by founders only.
168
*
169
* @return string The list of options.
170
*/
171
function group_select_options($group_id, $exclude_ids = false, $manage_founder = false)
172
{
173
        global $db, $user, $config;
174
175
        $exclude_sql = ($exclude_ids !== false && sizeof($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
176
        $sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
177
        $sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
178
179
        $sql = 'SELECT group_id, group_name, group_type
180
                FROM ' . GROUPS_TABLE . "
181
                $exclude_sql
182
                $sql_and
183
                $sql_founder
184
                ORDER BY group_type DESC, group_name ASC";
185
        $result = $db->sql_query($sql);
186
187
        $s_group_options = '';
188
        while ($row = $db->sql_fetchrow($result))
189
        {
190
                $selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : '';
191
                $s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
192
        }
193
        $db->sql_freeresult($result);
194
195
        return $s_group_options;
196
}
197
198
/**
199
* Obtain authed forums list
200
*/
201
function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false)
202
{
203
        global $db, $auth;
204
        static $forum_rows;
205
206
        if (!isset($forum_rows))
207
        {
208
                // This query is identical to the jumpbox one
209
                $expire_time = ($no_cache) ? 0 : 600;
210
211
                $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
212
                        FROM ' . FORUMS_TABLE . '
213
                        ORDER BY left_id ASC';
214
                $result = $db->sql_query($sql, $expire_time);
215
216
                $forum_rows = array();
217
218
                $right = $padding = 0;
219
                $padding_store = array('0' => 0);
220
221
                while ($row = $db->sql_fetchrow($result))
222
                {
223
                        if ($row['left_id'] < $right)
224
                        {
225
                                $padding++;
226
                                $padding_store[$row['parent_id']] = $padding;
227
                        }
228
                        else if ($row['left_id'] > $right + 1)
229
                        {
230
                                // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
231
                                // @todo digging deep to find out "how" this can happen.
232
                                $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
233
                        }
234
235
                        $right = $row['right_id'];
236
                        $row['padding'] = $padding;
237
238
                        $forum_rows[] = $row;
239
                }
240
                $db->sql_freeresult($result);
241
                unset($padding_store);
242
        }
243
244
        $rowset = array();
245
        foreach ($forum_rows as $row)
246
        {
247
                if ($postable_only && $row['forum_type'] != FORUM_POST)
248
                {
249
                        continue;
250
                }
251
252
                if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id'])))
253
                {
254
                        $rowset[] = ($id_only) ? (int) $row['forum_id'] : $row;
255
                }
256
        }
257
258
        return $rowset;
259
}
260
261
/**
262
* Get forum branch
263
*/
264
function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true)
265
{
266
        global $db;
267
268
        switch ($type)
269
        {
270
                case 'parents':
271
                        $condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
272
                break;
273
274
                case 'children':
275
                        $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
276
                break;
277
278
                default:
279
                        $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
280
                break;
281
        }
282
283
        $rows = array();
284
285
        $sql = 'SELECT f2.*
286
                FROM ' . FORUMS_TABLE . ' f1
287
                LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition)
288
                WHERE f1.forum_id = $forum_id
289
                ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
290
        $result = $db->sql_query($sql);
291
292
        while ($row = $db->sql_fetchrow($result))
293
        {
294
                if (!$include_forum && $row['forum_id'] == $forum_id)
295
                {
296
                        continue;
297
                }
298
299
                $rows[] = $row;
300
        }
301
        $db->sql_freeresult($result);
302
303
        return $rows;
304
}
305
306
/**
307
* Copies permissions from one forum to others
308
*
309
* @param int        $src_forum_id                The source forum we want to copy permissions from
310
* @param array        $dest_forum_ids                The destination forum(s) we want to copy to
311
* @param bool        $clear_dest_perms        True if destination permissions should be deleted
312
* @param bool        $add_log                        True if log entry should be added
313
*
314
* @return bool                                                False on error
315
*
316
* @author bantu
317
*/
318
function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true)
319
{
320
        global $db;
321
322
        // Only one forum id specified
323
        if (!is_array($dest_forum_ids))
324
        {
325
                $dest_forum_ids = array($dest_forum_ids);
326
        }
327
328
        // Make sure forum ids are integers
329
        $src_forum_id = (int) $src_forum_id;
330
        $dest_forum_ids = array_map('intval', $dest_forum_ids);
331
332
        // No source forum or no destination forums specified
333
        if (empty($src_forum_id) || empty($dest_forum_ids))
334
        {
335
                return false;
336
        }
337
338
        // Check if source forum exists
339
        $sql = 'SELECT forum_name
340
                FROM ' . FORUMS_TABLE . '
341
                WHERE forum_id = ' . $src_forum_id;
342
        $result = $db->sql_query($sql);
343
        $src_forum_name = $db->sql_fetchfield('forum_name');
344
        $db->sql_freeresult($result);
345
346
        // Source forum doesn't exist
347
        if (empty($src_forum_name))
348
        {
349
                return false;
350
        }
351
352
        // Check if destination forums exists
353
        $sql = 'SELECT forum_id, forum_name
354
                FROM ' . FORUMS_TABLE . '
355
                WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
356
        $result = $db->sql_query($sql);
357
358
        $dest_forum_ids = $dest_forum_names = array();
359
        while ($row = $db->sql_fetchrow($result))
360
        {
361
                $dest_forum_ids[]        = (int) $row['forum_id'];
362
                $dest_forum_names[]        = $row['forum_name'];
363
        }
364
        $db->sql_freeresult($result);
365
366
        // No destination forum exists
367
        if (empty($dest_forum_ids))
368
        {
369
                return false;
370
        }
371
372
        // From the mysql documentation:
373
        // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear
374
        // in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
375
        // Due to this we stay on the safe side if we do the insertion "the manual way"
376
377
        // Rowsets we're going to insert
378
        $users_sql_ary = $groups_sql_ary = array();
379
380
        // Query acl users table for source forum data
381
        $sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting
382
                FROM ' . ACL_USERS_TABLE . '
383
                WHERE forum_id = ' . $src_forum_id;
384
        $result = $db->sql_query($sql);
385
386
        while ($row = $db->sql_fetchrow($result))
387
        {
388
                $row = array(
389
                        'user_id'                        => (int) $row['user_id'],
390
                        'auth_option_id'        => (int) $row['auth_option_id'],
391
                        'auth_role_id'                => (int) $row['auth_role_id'],
392
                        'auth_setting'                => (int) $row['auth_setting'],
393
                );
394
395
                foreach ($dest_forum_ids as $dest_forum_id)
396
                {
397
                        $users_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
398
                }
399
        }
400
        $db->sql_freeresult($result);
401
402
        // Query acl groups table for source forum data
403
        $sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting
404
                FROM ' . ACL_GROUPS_TABLE . '
405
                WHERE forum_id = ' . $src_forum_id;
406
        $result = $db->sql_query($sql);
407
408
        while ($row = $db->sql_fetchrow($result))
409
        {
410
                $row = array(
411
                        'group_id'                        => (int) $row['group_id'],
412
                        'auth_option_id'        => (int) $row['auth_option_id'],
413
                        'auth_role_id'                => (int) $row['auth_role_id'],
414
                        'auth_setting'                => (int) $row['auth_setting'],
415
                );
416
417
                foreach ($dest_forum_ids as $dest_forum_id)
418
                {
419
                        $groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
420
                }
421
        }
422
        $db->sql_freeresult($result);
423
424
        $db->sql_transaction('begin');
425
426
        // Clear current permissions of destination forums
427
        if ($clear_dest_perms)
428
        {
429
                $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
430
                        WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
431
                $db->sql_query($sql);
432
433
                $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
434
                        WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
435
                $db->sql_query($sql);
436
        }
437
438
        $db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary);
439
        $db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary);
440
441
        if ($add_log)
442
        {
443
                add_log('admin', 'LOG_FORUM_COPIED_PERMISSIONS', $src_forum_name, implode(', ', $dest_forum_names));
444
        }
445
446
        $db->sql_transaction('commit');
447
448
        return true;
449
}
450
451
/**
452
* Get physical file listing
453
*/
454
function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png')
455
{
456
        $matches = array($dir => array());
457
458
        // Remove initial / if present
459
        $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir;
460
        // Add closing / if not present
461
        $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir;
462
463
        // Remove initial / if present
464
        $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir;
465
        // Add closing / if not present
466
        $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir;
467
468
        if (!is_dir($rootdir . $dir))
469
        {
470
                return $matches;
471
        }
472
473
        $dh = @opendir($rootdir . $dir);
474
475
        if (!$dh)
476
        {
477
                return $matches;
478
        }
479
480
        while (($fname = readdir($dh)) !== false)
481
        {
482
                if (is_file("$rootdir$dir$fname"))
483
                {
484
                        if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname))
485
                        {
486
                                $matches[$dir][] = $fname;
487
                        }
488
                }
489
                else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname"))
490
                {
491
                        $matches += filelist($rootdir, "$dir$fname", $type);
492
                }
493
        }
494
        closedir($dh);
495
496
        return $matches;
497
}
498
499
/**
500
* Move topic(s)
501
*/
502
function move_topics($topic_ids, $forum_id, $auto_sync = true)
503
{
504
        global $db;
505
506
        if (empty($topic_ids))
507
        {
508
                return;
509
        }
510
511
        $forum_ids = array($forum_id);
512
513
        if (!is_array($topic_ids))
514
        {
515
                $topic_ids = array($topic_ids);
516
        }
517
518
        $sql = 'DELETE FROM ' . TOPICS_TABLE . '
519
                WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . '
520
                        AND forum_id = ' . $forum_id;
521
        $db->sql_query($sql);
522
523
        if ($auto_sync)
524
        {
525
                $sql = 'SELECT DISTINCT forum_id
526
                        FROM ' . TOPICS_TABLE . '
527
                        WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
528
                $result = $db->sql_query($sql);
529
530
                while ($row = $db->sql_fetchrow($result))
531
                {
532
                        $forum_ids[] = $row['forum_id'];
533
                }
534
                $db->sql_freeresult($result);
535
        }
536
537
        $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
538
        foreach ($table_ary as $table)
539
        {
540
                $sql = "UPDATE $table
541
                        SET forum_id = $forum_id
542
                        WHERE " . $db->sql_in_set('topic_id', $topic_ids);
543
                $db->sql_query($sql);
544
        }
545
        unset($table_ary);
546
547
        if ($auto_sync)
548
        {
549
                sync('forum', 'forum_id', $forum_ids, true, true);
550
                unset($forum_ids);
551
        }
552
}
553
554
/**
555
* Move post(s)
556
*/
557
function move_posts($post_ids, $topic_id, $auto_sync = true)
558
{
559
        global $db;
560
561
        if (!is_array($post_ids))
562
        {
563
                $post_ids = array($post_ids);
564
        }
565
566
        $forum_ids = array();
567
        $topic_ids = array($topic_id);
568
569
        $sql = 'SELECT DISTINCT topic_id, forum_id
570
                FROM ' . POSTS_TABLE . '
571
                WHERE ' . $db->sql_in_set('post_id', $post_ids);
572
        $result = $db->sql_query($sql);
573
574
        while ($row = $db->sql_fetchrow($result))
575
        {
576
                $forum_ids[] = (int) $row['forum_id'];
577
                $topic_ids[] = (int) $row['topic_id'];
578
        }
579
        $db->sql_freeresult($result);
580
581
        $sql = 'SELECT forum_id
582
                FROM ' . TOPICS_TABLE . '
583
                WHERE topic_id = ' . $topic_id;
584
        $result = $db->sql_query($sql);
585
        $forum_row = $db->sql_fetchrow($result);
586
        $db->sql_freeresult($result);
587
588
        if (!$forum_row)
589
        {
590
                trigger_error('NO_TOPIC');
591
        }
592
593
        $sql = 'UPDATE ' . POSTS_TABLE . '
594
                SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id
595
                WHERE " . $db->sql_in_set('post_id', $post_ids);
596
        $db->sql_query($sql);
597
598
        $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
599
                SET topic_id = $topic_id, in_message = 0
600
                WHERE " . $db->sql_in_set('post_msg_id', $post_ids);
601
        $db->sql_query($sql);
602
603
        if ($auto_sync)
604
        {
605
                $forum_ids[] = (int) $forum_row['forum_id'];
606
607
                sync('topic_reported', 'topic_id', $topic_ids);
608
                sync('topic_attachment', 'topic_id', $topic_ids);
609
                sync('topic', 'topic_id', $topic_ids, true);
610
                sync('forum', 'forum_id', $forum_ids, true, true);
611
        }
612
613
        // Update posted information
614
        update_posted_info($topic_ids);
615
}
616
617
/**
618
* Remove topic(s)
619
*/
620
function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
621
{
622
        global $db, $config;
623
624
        $approved_topics = 0;
625
        $forum_ids = $topic_ids = array();
626
627
        if ($where_type === 'range')
628
        {
629
                $where_clause = $where_ids;
630
        }
631
        else
632
        {
633
                $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids);
634
635
                if (!sizeof($where_ids))
636
                {
637
                        return array('topics' => 0, 'posts' => 0);
638
                }
639
640
                $where_clause = $db->sql_in_set($where_type, $where_ids);
641
        }
642
643
        // Making sure that delete_posts does not call delete_topics again...
644
        $return = array(
645
                'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0,
646
        );
647
648
        $sql = 'SELECT topic_id, forum_id, topic_approved, topic_moved_id
649
                FROM ' . TOPICS_TABLE . '
650
                WHERE ' . $where_clause;
651
        $result = $db->sql_query($sql);
652
653
        while ($row = $db->sql_fetchrow($result))
654
        {
655
                $forum_ids[] = $row['forum_id'];
656
                $topic_ids[] = $row['topic_id'];
657
658
                if ($row['topic_approved'] && !$row['topic_moved_id'])
659
                {
660
                        $approved_topics++;
661
                }
662
        }
663
        $db->sql_freeresult($result);
664
665
        $return['topics'] = sizeof($topic_ids);
666
667
        if (!sizeof($topic_ids))
668
        {
669
                return $return;
670
        }
671
672
        $db->sql_transaction('begin');
673
674
        $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE);
675
676
        foreach ($table_ary as $table)
677
        {
678
                $sql = "DELETE FROM $table
679
                        WHERE " . $db->sql_in_set('topic_id', $topic_ids);
680
                $db->sql_query($sql);
681
        }
682
        unset($table_ary);
683
684
        $moved_topic_ids = array();
685
686
        // update the other forums
687
        $sql = 'SELECT topic_id, forum_id
688
                FROM ' . TOPICS_TABLE . '
689
                WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
690
        $result = $db->sql_query($sql);
691
692
        while ($row = $db->sql_fetchrow($result))
693
        {
694
                $forum_ids[] = $row['forum_id'];
695
                $moved_topic_ids[] = $row['topic_id'];
696
        }
697
        $db->sql_freeresult($result);
698
699
        if (sizeof($moved_topic_ids))
700
        {
701
                $sql = 'DELETE FROM ' . TOPICS_TABLE . '
702
                        WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids);
703
                $db->sql_query($sql);
704
        }
705
706
        $db->sql_transaction('commit');
707
708
        if ($auto_sync)
709
        {
710
                sync('forum', 'forum_id', array_unique($forum_ids), true, true);
711
                sync('topic_reported', $where_type, $where_ids);
712
        }
713
714
        if ($approved_topics)
715
        {
716
                set_config_count('num_topics', $approved_topics * (-1), true);
717
        }
718
719
        return $return;
720
}
721
722
/**
723
* Remove post(s)
724
*/
725
function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
726
{
727
        global $db, $config, $phpbb_root_path, $phpEx;
728
729
        if ($where_type === 'range')
730
        {
731
                $where_clause = $where_ids;
732
        }
733
        else
734
        {
735
                if (is_array($where_ids))
736
                {
737
                        $where_ids = array_unique($where_ids);
738
                }
739
                else
740
                {
741
                        $where_ids = array($where_ids);
742
                }
743
744
                if (!sizeof($where_ids))
745
                {
746
                        return false;
747
                }
748
749
                $where_ids = array_map('intval', $where_ids);
750
751
/*                Possible code for splitting post deletion
752
                if (sizeof($where_ids) >= 1001)
753
                {
754
                        // Split into chunks of 1000
755
                        $chunks = array_chunk($where_ids, 1000);
756
757
                        foreach ($chunks as $_where_ids)
758
                        {
759
                                delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics);
760
                        }
761
762
                        return;
763
                }*/
764
765
                $where_clause = $db->sql_in_set($where_type, $where_ids);
766
        }
767
768
        $approved_posts = 0;
769
        $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array();
770
771
        $sql = 'SELECT post_id, poster_id, post_approved, post_postcount, topic_id, forum_id
772
                FROM ' . POSTS_TABLE . '
773
                WHERE ' . $where_clause;
774
        $result = $db->sql_query($sql);
775
776
        while ($row = $db->sql_fetchrow($result))
777
        {
778
                $post_ids[] = (int) $row['post_id'];
779
                $poster_ids[] = (int) $row['poster_id'];
780
                $topic_ids[] = (int) $row['topic_id'];
781
                $forum_ids[] = (int) $row['forum_id'];
782
783
                if ($row['post_postcount'] && $post_count_sync && $row['post_approved'])
784
                {
785
                        $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1;
786
                }
787
788
                if ($row['post_approved'])
789
                {
790
                        $approved_posts++;
791
                }
792
        }
793
        $db->sql_freeresult($result);
794
795
        if (!sizeof($post_ids))
796
        {
797
                return false;
798
        }
799
800
        $db->sql_transaction('begin');
801
802
        $table_ary = array(POSTS_TABLE, REPORTS_TABLE);
803
804
        foreach ($table_ary as $table)
805
        {
806
                $sql = "DELETE FROM $table
807
                        WHERE " . $db->sql_in_set('post_id', $post_ids);
808
                $db->sql_query($sql);
809
        }
810
        unset($table_ary);
811
812
        // Adjust users post counts
813
        if (sizeof($post_counts) && $post_count_sync)
814
        {
815
                foreach ($post_counts as $poster_id => $substract)
816
                {
817
                        $sql = 'UPDATE ' . USERS_TABLE . '
818
                                SET user_posts = 0
819
                                WHERE user_id = ' . $poster_id . '
820
                                AND user_posts < ' . $substract;
821
                        $db->sql_query($sql);
822
823
                        $sql = 'UPDATE ' . USERS_TABLE . '
824
                                SET user_posts = user_posts - ' . $substract . '
825
                                WHERE user_id = ' . $poster_id . '
826
                                AND user_posts >= ' . $substract;
827
                        $db->sql_query($sql);
828
                }
829
        }
830
831
        // Remove topics now having no posts?
832
        if (sizeof($topic_ids))
833
        {
834
                $sql = 'SELECT topic_id
835
                        FROM ' . POSTS_TABLE . '
836
                        WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
837
                        GROUP BY topic_id';
838
                $result = $db->sql_query($sql);
839
840
                while ($row = $db->sql_fetchrow($result))
841
                {
842
                        $remove_topics[] = $row['topic_id'];
843
                }
844
                $db->sql_freeresult($result);
845
846
                // Actually, those not within remove_topics should be removed. ;)
847
                $remove_topics = array_diff($topic_ids, $remove_topics);
848
        }
849
850
        // Remove the message from the search index
851
        $search_type = basename($config['search_type']);
852
853
        if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx))
854
        {
855
                trigger_error('NO_SUCH_SEARCH_MODULE');
856
        }
857
858
        include_once("{$phpbb_root_path}includes/search/$search_type.$phpEx");
859
860
        $error = false;
861
        $search = new $search_type($error);
862
863
        if ($error)
864
        {
865
                trigger_error($error);
866
        }
867
868
        $search->index_remove($post_ids, $poster_ids, $forum_ids);
869
870
        delete_attachments('post', $post_ids, false);
871
872
        $db->sql_transaction('commit');
873
874
        // Resync topics_posted table
875
        if ($posted_sync)
876
        {
877
                update_posted_info($topic_ids);
878
        }
879
880
        if ($auto_sync)
881
        {
882
                sync('topic_reported', 'topic_id', $topic_ids);
883
                sync('topic', 'topic_id', $topic_ids, true);
884
                sync('forum', 'forum_id', $forum_ids, true, true);
885
        }
886
887
        if ($approved_posts)
888
        {
889
                set_config_count('num_posts', $approved_posts * (-1), true);
890
        }
891
892
        // We actually remove topics now to not be inconsistent (the delete_topics function calls this function too)
893
        if (sizeof($remove_topics) && $call_delete_topics)
894
        {
895
                delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);
896
        }
897
898
        return sizeof($post_ids);
899
}
900
901
/**
902
* Delete Attachments
903
*
904
* @param string $mode can be: post|message|topic|attach|user
905
* @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids
906
* @param bool $resync set this to false if you are deleting posts or topics
907
*/
908
function delete_attachments($mode, $ids, $resync = true)
909
{
910
        global $db, $config;
911
912
        // 0 is as bad as an empty array
913
        if (empty($ids))
914
        {
915
                return false;
916
        }
917
918
        if (is_array($ids))
919
        {
920
                $ids = array_unique($ids);
921
                $ids = array_map('intval', $ids);
922
        }
923
        else
924
        {
925
                $ids = array((int) $ids);
926
        }
927
928
        $sql_where = '';
929
930
        switch ($mode)
931
        {
932
                case 'post':
933
                case 'message':
934
                        $sql_id = 'post_msg_id';
935
                        $sql_where = ' AND in_message = ' . ($mode == 'message' ? 1 : 0);
936
                break;
937
938
                case 'topic':
939
                        $sql_id = 'topic_id';
940
                break;
941
942
                case 'user':
943
                        $sql_id = 'poster_id';
944
                break;
945
946
                case 'attach':
947
                default:
948
                        $sql_id = 'attach_id';
949
                        $mode = 'attach';
950
                break;
951
        }
952
953
        $post_ids = $message_ids = $topic_ids = $physical = array();
954
955
        // Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
956
        $sql = 'SELECT post_msg_id, topic_id, in_message, physical_filename, thumbnail, filesize, is_orphan
957
                        FROM ' . ATTACHMENTS_TABLE . '
958
                        WHERE ' . $db->sql_in_set($sql_id, $ids);
959
960
        $sql .= $sql_where;
961
962
        $result = $db->sql_query($sql);
963
964
        while ($row = $db->sql_fetchrow($result))
965
        {
966
                // We only need to store post/message/topic ids if resync is enabled and the file is not orphaned
967
                if ($resync && !$row['is_orphan'])
968
                {
969
                        if (!$row['in_message'])
970
                        {
971
                                $post_ids[] = $row['post_msg_id'];
972
                                $topic_ids[] = $row['topic_id'];
973
                        }
974
                        else
975
                        {
976
                                $message_ids[] = $row['post_msg_id'];
977
                        }
978
                }
979
980
                $physical[] = array('filename' => $row['physical_filename'], 'thumbnail' => $row['thumbnail'], 'filesize' => $row['filesize'], 'is_orphan' => $row['is_orphan']);
981
        }
982
        $db->sql_freeresult($result);
983
984
        // Delete attachments
985
        $sql = 'DELETE FROM ' . ATTACHMENTS_TABLE . '
986
                WHERE ' . $db->sql_in_set($sql_id, $ids);
987
988
        $sql .= $sql_where;
989
990
        $db->sql_query($sql);
991
        $num_deleted = $db->sql_affectedrows();
992
993
        if (!$num_deleted)
994
        {
995
                return 0;
996
        }
997
998
        // Delete attachments from filesystem
999
        $space_removed = $files_removed = 0;
1000
        foreach ($physical as $file_ary)
1001
        {
1002
                if (phpbb_unlink($file_ary['filename'], 'file', true) && !$file_ary['is_orphan'])
1003
                {
1004
                        // Only non-orphaned files count to the file size
1005
                        $space_removed += $file_ary['filesize'];
1006
                        $files_removed++;
1007
                }
1008
1009
                if ($file_ary['thumbnail'])
1010
                {
1011
                        phpbb_unlink($file_ary['filename'], 'thumbnail', true);
1012
                }
1013
        }
1014
1015
        if ($space_removed || $files_removed)
1016
        {
1017
                set_config_count('upload_dir_size', $space_removed * (-1), true);
1018
                set_config_count('num_files', $files_removed * (-1), true);
1019
        }
1020
1021
        // If we do not resync, we do not need to adjust any message, post, topic or user entries
1022
        if (!$resync)
1023
        {
1024
                return $num_deleted;
1025
        }
1026
1027
        // No more use for the original ids
1028
        unset($ids);
1029
1030
        // Now, we need to resync posts, messages, topics. We go through every one of them
1031
        $post_ids = array_unique($post_ids);
1032
        $message_ids = array_unique($message_ids);
1033
        $topic_ids = array_unique($topic_ids);
1034
1035
        // Update post indicators for posts now no longer having attachments
1036
        if (sizeof($post_ids))
1037
        {
1038
                // Just check which posts are still having an assigned attachment not orphaned by querying the attachments table
1039
                $sql = 'SELECT post_msg_id
1040
                        FROM ' . ATTACHMENTS_TABLE . '
1041
                        WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
1042
                                AND in_message = 0
1043
                                AND is_orphan = 0';
1044
                $result = $db->sql_query($sql);
1045
1046
                $remaining_ids = array();
1047
                while ($row = $db->sql_fetchrow($result))
1048
                {
1049
                        $remaining_ids[] = $row['post_msg_id'];
1050
                }
1051
                $db->sql_freeresult($result);
1052
1053
                // Now only unset those ids remaining
1054
                $post_ids = array_diff($post_ids, $remaining_ids);
1055
1056
                if (sizeof($post_ids))
1057
                {
1058
                        $sql = 'UPDATE ' . POSTS_TABLE . '
1059
                                SET post_attachment = 0
1060
                                WHERE ' . $db->sql_in_set('post_id', $post_ids);
1061
                        $db->sql_query($sql);
1062
                }
1063
        }
1064
1065
        // Update message table if messages are affected
1066
        if (sizeof($message_ids))
1067
        {
1068
                // Just check which messages are still having an assigned attachment not orphaned by querying the attachments table
1069
                $sql = 'SELECT post_msg_id
1070
                        FROM ' . ATTACHMENTS_TABLE . '
1071
                        WHERE ' . $db->sql_in_set('post_msg_id', $message_ids) . '
1072
                                AND in_message = 1
1073
                                AND is_orphan = 0';
1074
                $result = $db->sql_query($sql);
1075
1076
                $remaining_ids = array();
1077
                while ($row = $db->sql_fetchrow($result))
1078
                {
1079
                        $remaining_ids[] = $row['post_msg_id'];
1080
                }
1081
                $db->sql_freeresult($result);
1082
1083
                // Now only unset those ids remaining
1084
                $message_ids = array_diff($message_ids, $remaining_ids);
1085
1086
                if (sizeof($message_ids))
1087
                {
1088
                        $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1089
                                SET message_attachment = 0
1090
                                WHERE ' . $db->sql_in_set('msg_id', $message_ids);
1091
                        $db->sql_query($sql);
1092
                }
1093
        }
1094
1095
        // Now update the topics. This is a bit trickier, because there could be posts still having attachments within the topic
1096
        if (sizeof($topic_ids))
1097
        {
1098
                // Just check which topics are still having an assigned attachment not orphaned by querying the attachments table (much less entries expected)
1099
                $sql = 'SELECT topic_id
1100
                        FROM ' . ATTACHMENTS_TABLE . '
1101
                        WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
1102
                                AND is_orphan = 0';
1103
                $result = $db->sql_query($sql);
1104
1105
                $remaining_ids = array();
1106
                while ($row = $db->sql_fetchrow($result))
1107
                {
1108
                        $remaining_ids[] = $row['topic_id'];
1109
                }
1110
                $db->sql_freeresult($result);
1111
1112
                // Now only unset those ids remaining
1113
                $topic_ids = array_diff($topic_ids, $remaining_ids);
1114
1115
                if (sizeof($topic_ids))
1116
                {
1117
                        $sql = 'UPDATE ' . TOPICS_TABLE . '
1118
                                SET topic_attachment = 0
1119
                                WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1120
                        $db->sql_query($sql);
1121
                }
1122
        }
1123
1124
        return $num_deleted;
1125
}
1126
1127
/**
1128
* Deletes shadow topics pointing to a specified forum.
1129
*
1130
* @param int                $forum_id                The forum id
1131
* @param string                $sql_more                Additional WHERE statement, e.g. t.topic_time < (time() - 1234)
1132
* @param bool                $auto_sync                Will call sync() if this is true
1133
*
1134
* @return array                Array with affected forums
1135
*
1136
* @author bantu
1137
*/
1138
function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true)
1139
{
1140
        global $db;
1141
1142
        if (!$forum_id)
1143
        {
1144
                // Nothing to do.
1145
                return;
1146
        }
1147
1148
        // Set of affected forums we have to resync
1149
        $sync_forum_ids = array();
1150
1151
        // Amount of topics we select and delete at once.
1152
        $batch_size = 500;
1153
1154
        do
1155
        {
1156
                $sql = 'SELECT t2.forum_id, t2.topic_id
1157
                        FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t
1158
                        WHERE t2.topic_moved_id = t.topic_id
1159
                                AND t.forum_id = ' . (int) $forum_id . '
1160
                                ' . (($sql_more) ? 'AND ' . $sql_more : '');
1161
                $result = $db->sql_query_limit($sql, $batch_size);
1162
1163
                $topic_ids = array();
1164
                while ($row = $db->sql_fetchrow($result))
1165
                {
1166
                        $topic_ids[] = (int) $row['topic_id'];
1167
1168
                        $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
1169
                }
1170
                $db->sql_freeresult($result);
1171
1172
                if (!empty($topic_ids))
1173
                {
1174
                        $sql = 'DELETE FROM ' . TOPICS_TABLE . '
1175
                                WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1176
                        $db->sql_query($sql);
1177
                }
1178
        }
1179
        while (sizeof($topic_ids) == $batch_size);
1180
1181
        if ($auto_sync)
1182
        {
1183
                sync('forum', 'forum_id', $sync_forum_ids, true, true);
1184
        }
1185
1186
        return $sync_forum_ids;
1187
}
1188
1189
/**
1190
* Update/Sync posted information for topics
1191
*/
1192
function update_posted_info(&$topic_ids)
1193
{
1194
        global $db, $config;
1195
1196
        if (empty($topic_ids) || !$config['load_db_track'])
1197
        {
1198
                return;
1199
        }
1200
1201
        // First of all, let us remove any posted information for these topics
1202
        $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . '
1203
                WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1204
        $db->sql_query($sql);
1205
1206
        // Now, let us collect the user/topic combos for rebuilding the information
1207
        $sql = 'SELECT poster_id, topic_id
1208
                FROM ' . POSTS_TABLE . '
1209
                WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
1210
                        AND poster_id <> ' . ANONYMOUS . '
1211
                GROUP BY poster_id, topic_id';
1212
        $result = $db->sql_query($sql);
1213
1214
        $posted = array();
1215
        while ($row = $db->sql_fetchrow($result))
1216
        {
1217
                // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique
1218
                $posted[$row['poster_id']][] = $row['topic_id'];
1219
        }
1220
        $db->sql_freeresult($result);
1221
1222
        // Now add the information...
1223
        $sql_ary = array();
1224
        foreach ($posted as $user_id => $topic_row)
1225
        {
1226
                foreach ($topic_row as $topic_id)
1227
                {
1228
                        $sql_ary[] = array(
1229
                                'user_id'                => (int) $user_id,
1230
                                'topic_id'                => (int) $topic_id,
1231
                                'topic_posted'        => 1,
1232
                        );
1233
                }
1234
        }
1235
        unset($posted);
1236
1237
        $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary);
1238
}
1239
1240
/**
1241
* Delete attached file
1242
*/
1243
function phpbb_unlink($filename, $mode = 'file', $entry_removed = false)
1244
{
1245
        global $db, $phpbb_root_path, $config;
1246
1247
        // Because of copying topics or modifications a physical filename could be assigned more than once. If so, do not remove the file itself.
1248
        $sql = 'SELECT COUNT(attach_id) AS num_entries
1249
                FROM ' . ATTACHMENTS_TABLE . "
1250
                WHERE physical_filename = '" . $db->sql_escape(utf8_basename($filename)) . "'";
1251
        $result = $db->sql_query($sql);
1252
        $num_entries = (int) $db->sql_fetchfield('num_entries');
1253
        $db->sql_freeresult($result);
1254
1255
        // Do not remove file if at least one additional entry with the same name exist.
1256
        if (($entry_removed && $num_entries > 0) || (!$entry_removed && $num_entries > 1))
1257
        {
1258
                return false;
1259
        }
1260
1261
        $filename = ($mode == 'thumbnail') ? 'thumb_' . utf8_basename($filename) : utf8_basename($filename);
1262
        return @unlink($phpbb_root_path . $config['upload_path'] . '/' . $filename);
1263
}
1264
1265
/**
1266
* All-encompasing sync function
1267
*
1268
* Exaples:
1269
* <code>
1270
* sync('topic', 'topic_id', 123);                        // resync topic #123
1271
* sync('topic', 'forum_id', array(2, 3));        // resync topics from forum #2 and #3
1272
* sync('topic');                                                        // resync all topics
1273
* sync('topic', 'range', 'topic_id BETWEEN 1 AND 60');        // resync a range of topics/forums (only available for 'topic' and 'forum' modes)
1274
* </code>
1275
*
1276
* Modes:
1277
* - forum                                Resync complete forum
1278
* - topic                                Resync topics
1279
* - topic_moved                        Removes topic shadows that would be in the same forum as the topic they link to
1280
* - topic_approved                Resyncs the topic_approved flag according to the status of the first post
1281
* - post_reported                Resyncs the post_reported flag, relying on actual reports
1282
* - topic_reported                Resyncs the topic_reported flag, relying on post_reported flags
1283
* - post_attachement        Same as post_reported, but with attachment flags
1284
* - topic_attachement        Same as topic_reported, but with attachment flags
1285
*/
1286
function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false)
1287
{
1288
        global $db;
1289
1290
        if (is_array($where_ids))
1291
        {
1292
                $where_ids = array_unique($where_ids);
1293
                $where_ids = array_map('intval', $where_ids);
1294
        }
1295
        else if ($where_type != 'range')
1296
        {
1297
                $where_ids = ($where_ids) ? array((int) $where_ids) : array();
1298
        }
1299
1300
        if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_approved' || $mode == 'topic_reported' || $mode == 'post_reported')
1301
        {
1302
                if (!$where_type)
1303
                {
1304
                        $where_sql = '';
1305
                        $where_sql_and = 'WHERE';
1306
                }
1307
                else if ($where_type == 'range')
1308
                {
1309
                        // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60'
1310
                        $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)";
1311
                        $where_sql_and = $where_sql . "\n\tAND";
1312
                }
1313
                else
1314
                {
1315
                        // Do not sync the "global forum"
1316
                        $where_ids = array_diff($where_ids, array(0));
1317
1318
                        if (!sizeof($where_ids))
1319
                        {
1320
                                // Empty array with IDs. This means that we don't have any work to do. Just return.
1321
                                return;
1322
                        }
1323
1324
                        // Limit the topics/forums we are syncing, use specific topic/forum IDs.
1325
                        // $where_type contains the field for the where clause (forum_id, topic_id)
1326
                        $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
1327
                        $where_sql_and = $where_sql . "\n\tAND";
1328
                }
1329
        }
1330
        else
1331
        {
1332
                if (!sizeof($where_ids))
1333
                {
1334
                        return;
1335
                }
1336
1337
                // $where_type contains the field for the where clause (forum_id, topic_id)
1338
                $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
1339
                $where_sql_and = $where_sql . "\n\tAND";
1340
        }
1341
1342
        switch ($mode)
1343
        {
1344
                case 'topic_moved':
1345
                        $db->sql_transaction('begin');
1346
                        switch ($db->sql_layer)
1347
                        {
1348
                                case 'mysql4':
1349
                                case 'mysqli':
1350
                                        $sql = 'DELETE FROM ' . TOPICS_TABLE . '
1351
                                                USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
1352
                                                WHERE t1.topic_moved_id = t2.topic_id
1353
                                                        AND t1.forum_id = t2.forum_id";
1354
                                        $db->sql_query($sql);
1355
                                break;
1356
1357
                                default:
1358
                                        $sql = 'SELECT t1.topic_id
1359
                                                FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
1360
                                                WHERE t1.topic_moved_id = t2.topic_id
1361
                                                        AND t1.forum_id = t2.forum_id";
1362
                                        $result = $db->sql_query($sql);
1363
1364
                                        $topic_id_ary = array();
1365
                                        while ($row = $db->sql_fetchrow($result))
1366
                                        {
1367
                                                $topic_id_ary[] = $row['topic_id'];
1368
                                        }
1369
                                        $db->sql_freeresult($result);
1370
1371
                                        if (!sizeof($topic_id_ary))
1372
                                        {
1373
                                                return;
1374
                                        }
1375
1376
                                        $sql = 'DELETE FROM ' . TOPICS_TABLE . '
1377
                                                WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary);
1378
                                        $db->sql_query($sql);
1379
1380
                                break;
1381
                        }
1382
1383
                        $db->sql_transaction('commit');
1384
                        break;
1385
1386
                case 'topic_approved':
1387
1388
                        $db->sql_transaction('begin');
1389
                        switch ($db->sql_layer)
1390
                        {
1391
                                case 'mysql4':
1392
                                case 'mysqli':
1393
                                        $sql = 'UPDATE ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
1394
                                                SET t.topic_approved = p.post_approved
1395
                                                $where_sql_and t.topic_first_post_id = p.post_id";
1396
                                        $db->sql_query($sql);
1397
                                break;
1398
1399
                                default:
1400
                                        $sql = 'SELECT t.topic_id, p.post_approved
1401
                                                FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
1402
                                                $where_sql_and p.post_id = t.topic_first_post_id
1403
                                                        AND p.post_approved <> t.topic_approved";
1404
                                        $result = $db->sql_query($sql);
1405
1406
                                        $topic_ids = array();
1407
                                        while ($row = $db->sql_fetchrow($result))
1408
                                        {
1409
                                                $topic_ids[] = $row['topic_id'];
1410
                                        }
1411
                                        $db->sql_freeresult($result);
1412
1413
                                        if (!sizeof($topic_ids))
1414
                                        {
1415
                                                return;
1416
                                        }
1417
1418
                                        $sql = 'UPDATE ' . TOPICS_TABLE . '
1419
                                                SET topic_approved = 1 - topic_approved
1420
                                                WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1421
                                        $db->sql_query($sql);
1422
                                break;
1423
                        }
1424
1425
                        $db->sql_transaction('commit');
1426
                        break;
1427
1428
                case 'post_reported':
1429
                        $post_ids = $post_reported = array();
1430
1431
                        $db->sql_transaction('begin');
1432
1433
                        $sql = 'SELECT p.post_id, p.post_reported
1434
                                FROM ' . POSTS_TABLE . " p
1435
                                $where_sql
1436
                                GROUP BY p.post_id, p.post_reported";
1437
                        $result = $db->sql_query($sql);
1438
1439
                        while ($row = $db->sql_fetchrow($result))
1440
                        {
1441
                                $post_ids[$row['post_id']] = $row['post_id'];
1442
                                if ($row['post_reported'])
1443
                                {
1444
                                        $post_reported[$row['post_id']] = 1;
1445
                                }
1446
                        }
1447
                        $db->sql_freeresult($result);
1448
1449
                        $sql = 'SELECT DISTINCT(post_id)
1450
                                FROM ' . REPORTS_TABLE . '
1451
                                WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
1452
                                        AND report_closed = 0';
1453
                        $result = $db->sql_query($sql);
1454
1455
                        $post_ids = array();
1456
                        while ($row = $db->sql_fetchrow($result))
1457
                        {
1458
                                if (!isset($post_reported[$row['post_id']]))
1459
                                {
1460
                                        $post_ids[] = $row['post_id'];
1461
                                }
1462
                                else
1463
                                {
1464
                                        unset($post_reported[$row['post_id']]);
1465
                                }
1466
                        }
1467
                        $db->sql_freeresult($result);
1468
1469
                        // $post_reported should be empty by now, if it's not it contains
1470
                        // posts that are falsely flagged as reported
1471
                        foreach ($post_reported as $post_id => $void)
1472
                        {
1473
                                $post_ids[] = $post_id;
1474
                        }
1475
1476
                        if (sizeof($post_ids))
1477
                        {
1478
                                $sql = 'UPDATE ' . POSTS_TABLE . '
1479
                                        SET post_reported = 1 - post_reported
1480
                                        WHERE ' . $db->sql_in_set('post_id', $post_ids);
1481
                                $db->sql_query($sql);
1482
                        }
1483
1484
                        $db->sql_transaction('commit');
1485
                        break;
1486
1487
                case 'topic_reported':
1488
                        if ($sync_extra)
1489
                        {
1490
                                sync('post_reported', $where_type, $where_ids);
1491
                        }
1492
1493
                        $topic_ids = $topic_reported = array();
1494
1495
                        $db->sql_transaction('begin');
1496
1497
                        $sql = 'SELECT DISTINCT(t.topic_id)
1498
                                FROM ' . POSTS_TABLE . " t
1499
                                $where_sql_and t.post_reported = 1";
1500
                        $result = $db->sql_query($sql);
1501
1502
                        while ($row = $db->sql_fetchrow($result))
1503
                        {
1504
                                $topic_reported[$row['topic_id']] = 1;
1505
                        }
1506
                        $db->sql_freeresult($result);
1507
1508
                        $sql = 'SELECT t.topic_id, t.topic_reported
1509
                                FROM ' . TOPICS_TABLE . " t
1510
                                $where_sql";
1511
                        $result = $db->sql_query($sql);
1512
1513
                        while ($row = $db->sql_fetchrow($result))
1514
                        {
1515
                                if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']]))
1516
                                {
1517
                                        $topic_ids[] = $row['topic_id'];
1518
                                }
1519
                        }
1520
                        $db->sql_freeresult($result);
1521
1522
                        if (sizeof($topic_ids))
1523
                        {
1524
                                $sql = 'UPDATE ' . TOPICS_TABLE . '
1525
                                        SET topic_reported = 1 - topic_reported
1526
                                        WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1527
                                $db->sql_query($sql);
1528
                        }
1529
1530
                        $db->sql_transaction('commit');
1531
                        break;
1532
1533
                case 'post_attachment':
1534
                        $post_ids = $post_attachment = array();
1535
1536
                        $db->sql_transaction('begin');
1537
1538
                        $sql = 'SELECT p.post_id, p.post_attachment
1539
                                FROM ' . POSTS_TABLE . " p
1540
                                $where_sql
1541
                                GROUP BY p.post_id, p.post_attachment";
1542
                        $result = $db->sql_query($sql);
1543
1544
                        while ($row = $db->sql_fetchrow($result))
1545
                        {
1546
                                $post_ids[$row['post_id']] = $row['post_id'];
1547
                                if ($row['post_attachment'])
1548
                                {
1549
                                        $post_attachment[$row['post_id']] = 1;
1550
                                }
1551
                        }
1552
                        $db->sql_freeresult($result);
1553
1554
                        $sql = 'SELECT DISTINCT(post_msg_id)
1555
                                FROM ' . ATTACHMENTS_TABLE . '
1556
                                WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
1557
                                        AND in_message = 0';
1558
                        $result = $db->sql_query($sql);
1559
1560
                        $post_ids = array();
1561
                        while ($row = $db->sql_fetchrow($result))
1562
                        {
1563
                                if (!isset($post_attachment[$row['post_msg_id']]))
1564
                                {
1565
                                        $post_ids[] = $row['post_msg_id'];
1566
                                }
1567
                                else
1568
                                {
1569
                                        unset($post_attachment[$row['post_msg_id']]);
1570
                                }
1571
                        }
1572
                        $db->sql_freeresult($result);
1573
1574
                        // $post_attachment should be empty by now, if it's not it contains
1575
                        // posts that are falsely flagged as having attachments
1576
                        foreach ($post_attachment as $post_id => $void)
1577
                        {
1578
                                $post_ids[] = $post_id;
1579
                        }
1580
1581
                        if (sizeof($post_ids))
1582
                        {
1583
                                $sql = 'UPDATE ' . POSTS_TABLE . '
1584
                                        SET post_attachment = 1 - post_attachment
1585
                                        WHERE ' . $db->sql_in_set('post_id', $post_ids);
1586
                                $db->sql_query($sql);
1587
                        }
1588
1589
                        $db->sql_transaction('commit');
1590
                        break;
1591
1592
                case 'topic_attachment':
1593
                        if ($sync_extra)
1594
                        {
1595
                                sync('post_attachment', $where_type, $where_ids);
1596
                        }
1597
1598
                        $topic_ids = $topic_attachment = array();
1599
1600
                        $db->sql_transaction('begin');
1601
1602
                        $sql = 'SELECT DISTINCT(t.topic_id)
1603
                                FROM ' . POSTS_TABLE . " t
1604
                                $where_sql_and t.post_attachment = 1";
1605
                        $result = $db->sql_query($sql);
1606
1607
                        while ($row = $db->sql_fetchrow($result))
1608
                        {
1609
                                $topic_attachment[$row['topic_id']] = 1;
1610
                        }
1611
                        $db->sql_freeresult($result);
1612
1613
                        $sql = 'SELECT t.topic_id, t.topic_attachment
1614
                                FROM ' . TOPICS_TABLE . " t
1615
                                $where_sql";
1616
                        $result = $db->sql_query($sql);
1617
1618
                        while ($row = $db->sql_fetchrow($result))
1619
                        {
1620
                                if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']]))
1621
                                {
1622
                                        $topic_ids[] = $row['topic_id'];
1623
                                }
1624
                        }
1625
                        $db->sql_freeresult($result);
1626
1627
                        if (sizeof($topic_ids))
1628
                        {
1629
                                $sql = 'UPDATE ' . TOPICS_TABLE . '
1630
                                        SET topic_attachment = 1 - topic_attachment
1631
                                        WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1632
                                $db->sql_query($sql);
1633
                        }
1634
1635
                        $db->sql_transaction('commit');
1636
1637
                        break;
1638
1639
                case 'forum':
1640
1641
                        $db->sql_transaction('begin');
1642
1643
                        // 1: Get the list of all forums
1644
                        $sql = 'SELECT f.*
1645
                                FROM ' . FORUMS_TABLE . " f
1646
                                $where_sql";
1647
                        $result = $db->sql_query($sql);
1648
1649
                        $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array();
1650
                        while ($row = $db->sql_fetchrow($result))
1651
                        {
1652
                                if ($row['forum_type'] == FORUM_LINK)
1653
                                {
1654
                                        continue;
1655
                                }
1656
1657
                                $forum_id = (int) $row['forum_id'];
1658
                                $forum_ids[$forum_id] = $forum_id;
1659
1660
                                $forum_data[$forum_id] = $row;
1661
                                if ($sync_extra)
1662
                                {
1663
                                        $forum_data[$forum_id]['posts'] = 0;
1664
                                        $forum_data[$forum_id]['topics'] = 0;
1665
                                        $forum_data[$forum_id]['topics_real'] = 0;
1666
                                }
1667
                                $forum_data[$forum_id]['last_post_id'] = 0;
1668
                                $forum_data[$forum_id]['last_post_subject'] = '';
1669
                                $forum_data[$forum_id]['last_post_time'] = 0;
1670
                                $forum_data[$forum_id]['last_poster_id'] = 0;
1671
                                $forum_data[$forum_id]['last_poster_name'] = '';
1672
                                $forum_data[$forum_id]['last_poster_colour'] = '';
1673
                        }
1674
                        $db->sql_freeresult($result);
1675
1676
                        if (!sizeof($forum_ids))
1677
                        {
1678
                                break;
1679
                        }
1680
1681
                        $forum_ids = array_values($forum_ids);
1682
1683
                        // 2: Get topic counts for each forum (optional)
1684
                        if ($sync_extra)
1685
                        {
1686
                                $sql = 'SELECT forum_id, topic_approved, COUNT(topic_id) AS forum_topics
1687
                                        FROM ' . TOPICS_TABLE . '
1688
                                        WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . '
1689
                                        GROUP BY forum_id, topic_approved';
1690
                                $result = $db->sql_query($sql);
1691
1692
                                while ($row = $db->sql_fetchrow($result))
1693
                                {
1694
                                        $forum_id = (int) $row['forum_id'];
1695
                                        $forum_data[$forum_id]['topics_real'] += $row['forum_topics'];
1696
1697
                                        if ($row['topic_approved'])
1698
                                        {
1699
                                                $forum_data[$forum_id]['topics'] = $row['forum_topics'];
1700
                                        }
1701
                                }
1702
                                $db->sql_freeresult($result);
1703
                        }
1704
1705
                        // 3: Get post count for each forum (optional)
1706
                        if ($sync_extra)
1707
                        {
1708
                                if (sizeof($forum_ids) == 1)
1709
                                {
1710
                                        $sql = 'SELECT SUM(t.topic_replies + 1) AS forum_posts
1711
                                                FROM ' . TOPICS_TABLE . ' t
1712
                                                WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1713
                                                        AND t.topic_approved = 1
1714
                                                        AND t.topic_status <> ' . ITEM_MOVED;
1715
                                }
1716
                                else
1717
                                {
1718
                                        $sql = 'SELECT t.forum_id, SUM(t.topic_replies + 1) AS forum_posts
1719
                                                FROM ' . TOPICS_TABLE . ' t
1720
                                                WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1721
                                                        AND t.topic_approved = 1
1722
                                                        AND t.topic_status <> ' . ITEM_MOVED . '
1723
                                                GROUP BY t.forum_id';
1724
                                }
1725
1726
                                $result = $db->sql_query($sql);
1727
1728
                                while ($row = $db->sql_fetchrow($result))
1729
                                {
1730
                                        $forum_id = (sizeof($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
1731
1732
                                        $forum_data[$forum_id]['posts'] = (int) $row['forum_posts'];
1733
                                }
1734
                                $db->sql_freeresult($result);
1735
                        }
1736
1737
                        // 4: Get last_post_id for each forum
1738
                        if (sizeof($forum_ids) == 1)
1739
                        {
1740
                                $sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id
1741
                                        FROM ' . TOPICS_TABLE . ' t
1742
                                        WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1743
                                                AND t.topic_approved = 1';
1744
                        }
1745
                        else
1746
                        {
1747
                                $sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id
1748
                                        FROM ' . TOPICS_TABLE . ' t
1749
                                        WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1750
                                                AND t.topic_approved = 1
1751
                                        GROUP BY t.forum_id';
1752
                        }
1753
1754
                        $result = $db->sql_query($sql);
1755
1756
                        while ($row = $db->sql_fetchrow($result))
1757
                        {
1758
                                $forum_id = (sizeof($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
1759
1760
                                $forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id'];
1761
1762
                                $post_ids[] = $row['last_post_id'];
1763
                        }
1764
                        $db->sql_freeresult($result);
1765
1766
                        // 5: Retrieve last_post infos
1767
                        if (sizeof($post_ids))
1768
                        {
1769
                                $sql = 'SELECT p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour
1770
                                        FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
1771
                                        WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
1772
                                                AND p.poster_id = u.user_id';
1773
                                $result = $db->sql_query($sql);
1774
1775
                                while ($row = $db->sql_fetchrow($result))
1776
                                {
1777
                                        $post_info[$row['post_id']] = $row;
1778
                                }
1779
                                $db->sql_freeresult($result);
1780
1781
                                foreach ($forum_data as $forum_id => $data)
1782
                                {
1783
                                        if ($data['last_post_id'])
1784
                                        {
1785
                                                if (isset($post_info[$data['last_post_id']]))
1786
                                                {
1787
                                                        $forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject'];
1788
                                                        $forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time'];
1789
                                                        $forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id'];
1790
                                                        $forum_data[$forum_id]['last_poster_name'] = ($post_info[$data['last_post_id']]['poster_id'] != ANONYMOUS) ? $post_info[$data['last_post_id']]['username'] : $post_info[$data['last_post_id']]['post_username'];
1791
                                                        $forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour'];
1792
                                                }
1793
                                                else
1794
                                                {
1795
                                                        // For some reason we did not find the post in the db
1796
                                                        $forum_data[$forum_id]['last_post_id'] = 0;
1797
                                                        $forum_data[$forum_id]['last_post_subject'] = '';
1798
                                                        $forum_data[$forum_id]['last_post_time'] = 0;
1799
                                                        $forum_data[$forum_id]['last_poster_id'] = 0;
1800
                                                        $forum_data[$forum_id]['last_poster_name'] = '';
1801
                                                        $forum_data[$forum_id]['last_poster_colour'] = '';
1802
                                                }
1803
                                        }
1804
                                }
1805
                                unset($post_info);
1806
                        }
1807
1808
                        // 6: Now do that thing
1809
                        $fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
1810
1811
                        if ($sync_extra)
1812
                        {
1813
                                array_push($fieldnames, 'posts', 'topics', 'topics_real');
1814
                        }
1815
1816
                        foreach ($forum_data as $forum_id => $row)
1817
                        {
1818
                                $sql_ary = array();
1819
1820
                                foreach ($fieldnames as $fieldname)
1821
                                {
1822
                                        if ($row['forum_' . $fieldname] != $row[$fieldname])
1823
                                        {
1824
                                                if (preg_match('#(name|colour|subject)$#', $fieldname))
1825
                                                {
1826
                                                        $sql_ary['forum_' . $fieldname] = (string) $row[$fieldname];
1827
                                                }
1828
                                                else
1829
                                                {
1830
                                                        $sql_ary['forum_' . $fieldname] = (int) $row[$fieldname];
1831
                                                }
1832
                                        }
1833
                                }
1834
1835
                                if (sizeof($sql_ary))
1836
                                {
1837
                                        $sql = 'UPDATE ' . FORUMS_TABLE . '
1838
                                                SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1839
                                                WHERE forum_id = ' . $forum_id;
1840
                                        $db->sql_query($sql);
1841
                                }
1842
                        }
1843
1844
                        $db->sql_transaction('commit');
1845
                        break;
1846
1847
                case 'topic':
1848
                        $topic_data = $post_ids = $approved_unapproved_ids = $resync_forums = $delete_topics = $delete_posts = $moved_topics = array();
1849
1850
                        $db->sql_transaction('begin');
1851
1852
                        $sql = 'SELECT t.topic_id, t.forum_id, t.topic_moved_id, t.topic_approved, ' . (($sync_extra) ? 't.topic_attachment, t.topic_reported, ' : '') . 't.topic_poster, t.topic_time, t.topic_replies, t.topic_replies_real, t.topic_first_post_id, t.topic_first_poster_name, t.topic_first_poster_colour, t.topic_last_post_id, t.topic_last_post_subject, t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_poster_colour, t.topic_last_post_time
1853
                                FROM ' . TOPICS_TABLE . " t
1854
                                $where_sql";
1855
                        $result = $db->sql_query($sql);
1856
1857
                        while ($row = $db->sql_fetchrow($result))
1858
                        {
1859
                                if ($row['topic_moved_id'])
1860
                                {
1861
                                        $moved_topics[] = $row['topic_id'];
1862
                                        continue;
1863
                                }
1864
1865
                                $topic_id = (int) $row['topic_id'];
1866
                                $topic_data[$topic_id] = $row;
1867
                                $topic_data[$topic_id]['replies_real'] = -1;
1868
                                $topic_data[$topic_id]['replies'] = 0;
1869
                                $topic_data[$topic_id]['first_post_id'] = 0;
1870
                                $topic_data[$topic_id]['last_post_id'] = 0;
1871
                                unset($topic_data[$topic_id]['topic_id']);
1872
1873
                                // This array holds all topic_ids
1874
                                $delete_topics[$topic_id] = '';
1875
1876
                                if ($sync_extra)
1877
                                {
1878
                                        $topic_data[$topic_id]['reported'] = 0;
1879
                                        $topic_data[$topic_id]['attachment'] = 0;
1880
                                }
1881
                        }
1882
                        $db->sql_freeresult($result);
1883
1884
                        // Use "t" as table alias because of the $where_sql clause
1885
                        // NOTE: 't.post_approved' in the GROUP BY is causing a major slowdown.
1886
                        $sql = 'SELECT t.topic_id, t.post_approved, COUNT(t.post_id) AS total_posts, MIN(t.post_id) AS first_post_id, MAX(t.post_id) AS last_post_id
1887
                                FROM ' . POSTS_TABLE . " t
1888
                                $where_sql
1889
                                GROUP BY t.topic_id, t.post_approved";
1890
                        $result = $db->sql_query($sql);
1891
1892
                        while ($row = $db->sql_fetchrow($result))
1893
                        {
1894
                                $topic_id = (int) $row['topic_id'];
1895
1896
                                $row['first_post_id'] = (int) $row['first_post_id'];
1897
                                $row['last_post_id'] = (int) $row['last_post_id'];
1898
1899
                                if (!isset($topic_data[$topic_id]))
1900
                                {
1901
                                        // Hey, these posts come from a topic that does not exist
1902
                                        $delete_posts[$topic_id] = '';
1903
                                }
1904
                                else
1905
                                {
1906
                                        // Unset the corresponding entry in $delete_topics
1907
                                        // When we'll be done, only topics with no posts will remain
1908
                                        unset($delete_topics[$topic_id]);
1909
1910
                                        $topic_data[$topic_id]['replies_real'] += $row['total_posts'];
1911
                                        $topic_data[$topic_id]['first_post_id'] = (!$topic_data[$topic_id]['first_post_id']) ? $row['first_post_id'] : min($topic_data[$topic_id]['first_post_id'], $row['first_post_id']);
1912
1913
                                        if ($row['post_approved'] || !$topic_data[$topic_id]['last_post_id'])
1914
                                        {
1915
                                                $topic_data[$topic_id]['replies'] = $row['total_posts'] - 1;
1916
                                                $topic_data[$topic_id]['last_post_id'] = $row['last_post_id'];
1917
                                        }
1918
                                }
1919
                        }
1920
                        $db->sql_freeresult($result);
1921
1922
                        foreach ($topic_data as $topic_id => $row)
1923
                        {
1924
                                $post_ids[] = $row['first_post_id'];
1925
                                if ($row['first_post_id'] != $row['last_post_id'])
1926
                                {
1927
                                        $post_ids[] = $row['last_post_id'];
1928
                                }
1929
                        }
1930
1931
                        // Now we delete empty topics and orphan posts
1932
                        if (sizeof($delete_posts))
1933
                        {
1934
                                delete_posts('topic_id', array_keys($delete_posts), false);
1935
                                unset($delete_posts);
1936
                        }
1937
1938
                        if (!sizeof($topic_data))
1939
                        {
1940
                                // If we get there, topic ids were invalid or topics did not contain any posts
1941
                                delete_topics($where_type, $where_ids, true);
1942
                                return;
1943
                        }
1944
1945
                        if (sizeof($delete_topics))
1946
                        {
1947
                                $delete_topic_ids = array();
1948
                                foreach ($delete_topics as $topic_id => $void)
1949
                                {
1950
                                        unset($topic_data[$topic_id]);
1951
                                        $delete_topic_ids[] = $topic_id;
1952
                                }
1953
1954
                                delete_topics('topic_id', $delete_topic_ids, false);
1955
                                unset($delete_topics, $delete_topic_ids);
1956
                        }
1957
1958
                        $sql = 'SELECT p.post_id, p.topic_id, p.post_approved, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour
1959
                                FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
1960
                                WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
1961
                                        AND u.user_id = p.poster_id';
1962
                        $result = $db->sql_query($sql);
1963
1964
                        $post_ids = array();
1965
                        while ($row = $db->sql_fetchrow($result))
1966
                        {
1967
                                $topic_id = intval($row['topic_id']);
1968
1969
                                if ($row['post_id'] == $topic_data[$topic_id]['first_post_id'])
1970
                                {
1971
                                        if ($topic_data[$topic_id]['topic_approved'] != $row['post_approved'])
1972
                                        {
1973
                                                $approved_unapproved_ids[] = $topic_id;
1974
                                        }
1975
                                        $topic_data[$topic_id]['time'] = $row['post_time'];
1976
                                        $topic_data[$topic_id]['poster'] = $row['poster_id'];
1977
                                        $topic_data[$topic_id]['first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
1978
                                        $topic_data[$topic_id]['first_poster_colour'] = $row['user_colour'];
1979
                                }
1980
1981
                                if ($row['post_id'] == $topic_data[$topic_id]['last_post_id'])
1982
                                {
1983
                                        $topic_data[$topic_id]['last_poster_id'] = $row['poster_id'];
1984
                                        $topic_data[$topic_id]['last_post_subject'] = $row['post_subject'];
1985
                                        $topic_data[$topic_id]['last_post_time'] = $row['post_time'];
1986
                                        $topic_data[$topic_id]['last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
1987
                                        $topic_data[$topic_id]['last_poster_colour'] = $row['user_colour'];
1988
                                }
1989
                        }
1990
                        $db->sql_freeresult($result);
1991
1992
                        // Make sure shadow topics do link to existing topics
1993
                        if (sizeof($moved_topics))
1994
                        {
1995
                                $delete_topics = array();
1996
1997
                                $sql = 'SELECT t1.topic_id, t1.topic_moved_id
1998
                                        FROM ' . TOPICS_TABLE . ' t1
1999
                                        LEFT JOIN ' . TOPICS_TABLE . ' t2 ON (t2.topic_id = t1.topic_moved_id)
2000
                                        WHERE ' . $db->sql_in_set('t1.topic_id', $moved_topics) . '
2001
                                                AND t2.topic_id IS NULL';
2002
                                $result = $db->sql_query($sql);
2003
2004
                                while ($row = $db->sql_fetchrow($result))
2005
                                {
2006
                                        $delete_topics[] = $row['topic_id'];
2007
                                }
2008
                                $db->sql_freeresult($result);
2009
2010
                                if (sizeof($delete_topics))
2011
                                {
2012
                                        delete_topics('topic_id', $delete_topics, false);
2013
                                }
2014
                                unset($delete_topics);
2015
2016
                                // Make sure shadow topics having no last post data being updated (this only rarely happens...)
2017
                                $sql = 'SELECT topic_id, topic_moved_id, topic_last_post_id, topic_first_post_id
2018
                                        FROM ' . TOPICS_TABLE . '
2019
                                        WHERE ' . $db->sql_in_set('topic_id', $moved_topics) . '
2020
                                                AND topic_last_post_time = 0';
2021
                                $result = $db->sql_query($sql);
2022
2023
                                $shadow_topic_data = $post_ids = array();
2024
                                while ($row = $db->sql_fetchrow($result))
2025
                                {
2026
                                        $shadow_topic_data[$row['topic_moved_id']] = $row;
2027
                                        $post_ids[] = $row['topic_last_post_id'];
2028
                                        $post_ids[] = $row['topic_first_post_id'];
2029
                                }
2030
                                $db->sql_freeresult($result);
2031
2032
                                $sync_shadow_topics = array();
2033
                                if (sizeof($post_ids))
2034
                                {
2035
                                        $sql = 'SELECT p.post_id, p.topic_id, p.post_approved, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour
2036
                                                FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
2037
                                                WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
2038
                                                        AND u.user_id = p.poster_id';
2039
                                        $result = $db->sql_query($sql);
2040
2041
                                        $post_ids = array();
2042
                                        while ($row = $db->sql_fetchrow($result))
2043
                                        {
2044
                                                $topic_id = (int) $row['topic_id'];
2045
2046
                                                // Ok, there should be a shadow topic. If there isn't, then there's something wrong with the db.
2047
                                                // However, there's not much we can do about it.
2048
                                                if (!empty($shadow_topic_data[$topic_id]))
2049
                                                {
2050
                                                        if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_first_post_id'])
2051
                                                        {
2052
                                                                $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id'];
2053
2054
                                                                if (!isset($sync_shadow_topics[$orig_topic_id]))
2055
                                                                {
2056
                                                                        $sync_shadow_topics[$orig_topic_id] = array();
2057
                                                                }
2058
2059
                                                                $sync_shadow_topics[$orig_topic_id]['topic_time'] = $row['post_time'];
2060
                                                                $sync_shadow_topics[$orig_topic_id]['topic_poster'] = $row['poster_id'];
2061
                                                                $sync_shadow_topics[$orig_topic_id]['topic_first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
2062
                                                                $sync_shadow_topics[$orig_topic_id]['topic_first_poster_colour'] = $row['user_colour'];
2063
                                                        }
2064
2065
                                                        if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_last_post_id'])
2066
                                                        {
2067
                                                                $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id'];
2068
2069
                                                                if (!isset($sync_shadow_topics[$orig_topic_id]))
2070
                                                                {
2071
                                                                        $sync_shadow_topics[$orig_topic_id] = array();
2072
                                                                }
2073
2074
                                                                $sync_shadow_topics[$orig_topic_id]['topic_last_poster_id'] = $row['poster_id'];
2075
                                                                $sync_shadow_topics[$orig_topic_id]['topic_last_post_subject'] = $row['post_subject'];
2076
                                                                $sync_shadow_topics[$orig_topic_id]['topic_last_post_time'] = $row['post_time'];
2077
                                                                $sync_shadow_topics[$orig_topic_id]['topic_last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
2078
                                                                $sync_shadow_topics[$orig_topic_id]['topic_last_poster_colour'] = $row['user_colour'];
2079
                                                        }
2080
                                                }
2081
                                        }
2082
                                        $db->sql_freeresult($result);
2083
2084
                                        $shadow_topic_data = array();
2085
2086
                                        // Update the information we collected
2087
                                        if (sizeof($sync_shadow_topics))
2088
                                        {
2089
                                                foreach ($sync_shadow_topics as $sync_topic_id => $sql_ary)
2090
                                                {
2091
                                                        $sql = 'UPDATE ' . TOPICS_TABLE . '
2092
                                                                SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
2093
                                                                WHERE topic_id = ' . $sync_topic_id;
2094
                                                        $db->sql_query($sql);
2095
                                                }
2096
                                        }
2097
                                }
2098
2099
                                unset($sync_shadow_topics, $shadow_topic_data);
2100
                        }
2101
2102
                        // approved becomes unapproved, and vice-versa
2103
                        if (sizeof($approved_unapproved_ids))
2104
                        {
2105
                                $sql = 'UPDATE ' . TOPICS_TABLE . '
2106
                                        SET topic_approved = 1 - topic_approved
2107
                                        WHERE ' . $db->sql_in_set('topic_id', $approved_unapproved_ids);
2108
                                $db->sql_query($sql);
2109
                        }
2110
                        unset($approved_unapproved_ids);
2111
2112
                        // These are fields that will be synchronised
2113
                        $fieldnames = array('time', 'replies', 'replies_real', 'poster', 'first_post_id', 'first_poster_name', 'first_poster_colour', 'last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
2114
2115
                        if ($sync_extra)
2116
                        {
2117
                                // This routine assumes that post_reported values are correct
2118
                                // if they are not, use sync('post_reported') first
2119
                                $sql = 'SELECT t.topic_id, p.post_id
2120
                                        FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
2121
                                        $where_sql_and p.topic_id = t.topic_id
2122
                                                AND p.post_reported = 1
2123
                                        GROUP BY t.topic_id, p.post_id";
2124
                                $result = $db->sql_query($sql);
2125
2126
                                $fieldnames[] = 'reported';
2127
                                while ($row = $db->sql_fetchrow($result))
2128
                                {
2129
                                        $topic_data[intval($row['topic_id'])]['reported'] = 1;
2130
                                }
2131
                                $db->sql_freeresult($result);
2132
2133
                                // This routine assumes that post_attachment values are correct
2134
                                // if they are not, use sync('post_attachment') first
2135
                                $sql = 'SELECT t.topic_id, p.post_id
2136
                                        FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
2137
                                        $where_sql_and p.topic_id = t.topic_id
2138
                                                AND p.post_attachment = 1
2139
                                        GROUP BY t.topic_id, p.post_id";
2140
                                $result = $db->sql_query($sql);
2141
2142
                                $fieldnames[] = 'attachment';
2143
                                while ($row = $db->sql_fetchrow($result))
2144
                                {
2145
                                        $topic_data[intval($row['topic_id'])]['attachment'] = 1;
2146
                                }
2147
                                $db->sql_freeresult($result);
2148
                        }
2149
2150
                        foreach ($topic_data as $topic_id => $row)
2151
                        {
2152
                                $sql_ary = array();
2153
2154
                                foreach ($fieldnames as $fieldname)
2155
                                {
2156
                                        if (isset($row[$fieldname]) && isset($row['topic_' . $fieldname]) && $row['topic_' . $fieldname] != $row[$fieldname])
2157
                                        {
2158
                                                $sql_ary['topic_' . $fieldname] = $row[$fieldname];
2159
                                        }
2160
                                }
2161
2162
                                if (sizeof($sql_ary))
2163
                                {
2164
                                        $sql = 'UPDATE ' . TOPICS_TABLE . '
2165
                                                SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
2166
                                                WHERE topic_id = ' . $topic_id;
2167
                                        $db->sql_query($sql);
2168
2169
                                        $resync_forums[$row['forum_id']] = $row['forum_id'];
2170
                                }
2171
                        }
2172
                        unset($topic_data);
2173
2174
                        $db->sql_transaction('commit');
2175
2176
                        // if some topics have been resync'ed then resync parent forums
2177
                        // except when we're only syncing a range, we don't want to sync forums during
2178
                        // batch processing.
2179
                        if ($resync_parents && sizeof($resync_forums) && $where_type != 'range')
2180
                        {
2181
                                sync('forum', 'forum_id', array_values($resync_forums), true, true);
2182
                        }
2183
                        break;
2184
        }
2185
2186
        return;
2187
}
2188
2189
/**
2190
* Prune function
2191
*/
2192
function prune($forum_id, $prune_mode, $prune_date, $prune_flags = 0, $auto_sync = true)
2193
{
2194
        global $db;
2195
2196
        if (!is_array($forum_id))
2197
        {
2198
                $forum_id = array($forum_id);
2199
        }
2200
2201
        if (!sizeof($forum_id))
2202
        {
2203
                return;
2204
        }
2205
2206
        $sql_and = '';
2207
2208
        if (!($prune_flags & FORUM_FLAG_PRUNE_ANNOUNCE))
2209
        {
2210
                $sql_and .= ' AND topic_type <> ' . POST_ANNOUNCE;
2211
        }
2212
2213
        if (!($prune_flags & FORUM_FLAG_PRUNE_STICKY))
2214
        {
2215
                $sql_and .= ' AND topic_type <> ' . POST_STICKY;
2216
        }
2217
2218
        if ($prune_mode == 'posted')
2219
        {
2220
                $sql_and .= " AND topic_last_post_time < $prune_date";
2221
        }
2222
2223
        if ($prune_mode == 'viewed')
2224
        {
2225
                $sql_and .= " AND topic_last_view_time < $prune_date";
2226
        }
2227
2228
        $sql = 'SELECT topic_id
2229
                FROM ' . TOPICS_TABLE . '
2230
                WHERE ' . $db->sql_in_set('forum_id', $forum_id) . "
2231
                        AND poll_start = 0
2232
                        $sql_and";
2233
        $result = $db->sql_query($sql);
2234
2235
        $topic_list = array();
2236
        while ($row = $db->sql_fetchrow($result))
2237
        {
2238
                $topic_list[] = $row['topic_id'];
2239
        }
2240
        $db->sql_freeresult($result);
2241
2242
        if ($prune_flags & FORUM_FLAG_PRUNE_POLL)
2243
        {
2244
                $sql = 'SELECT topic_id
2245
                        FROM ' . TOPICS_TABLE . '
2246
                        WHERE ' . $db->sql_in_set('forum_id', $forum_id) . "
2247
                                AND poll_start > 0
2248
                                AND poll_last_vote < $prune_date
2249
                                $sql_and";
2250
                $result = $db->sql_query($sql);
2251
2252
                while ($row = $db->sql_fetchrow($result))
2253
                {
2254
                        $topic_list[] = $row['topic_id'];
2255
                }
2256
                $db->sql_freeresult($result);
2257
2258
                $topic_list = array_unique($topic_list);
2259
        }
2260
2261
        return delete_topics('topic_id', $topic_list, $auto_sync, false);
2262
}
2263
2264
/**
2265
* Function auto_prune(), this function now relies on passed vars
2266
*/
2267
function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq)
2268
{
2269
        global $db;
2270
2271
        $sql = 'SELECT forum_name
2272
                FROM ' . FORUMS_TABLE . "
2273
                WHERE forum_id = $forum_id";
2274
        $result = $db->sql_query($sql, 3600);
2275
        $row = $db->sql_fetchrow($result);
2276
        $db->sql_freeresult($result);
2277
2278
        if ($row)
2279
        {
2280
                $prune_date = time() - ($prune_days * 86400);
2281
                $next_prune = time() + ($prune_freq * 86400);
2282
2283
                prune($forum_id, $prune_mode, $prune_date, $prune_flags, true);
2284
2285
                $sql = 'UPDATE ' . FORUMS_TABLE . "
2286
                        SET prune_next = $next_prune
2287
                        WHERE forum_id = $forum_id";
2288
                $db->sql_query($sql);
2289
2290
                add_log('admin', 'LOG_AUTO_PRUNE', $row['forum_name']);
2291
        }
2292
2293
        return;
2294
}
2295
2296
/**
2297
* Cache moderators, called whenever permissions are changed via admin_permissions. Changes of username
2298
* and group names must be carried through for the moderators table
2299
*/
2300
function cache_moderators()
2301
{
2302
        global $db, $cache, $auth, $phpbb_root_path, $phpEx;
2303
2304
        // Remove cached sql results
2305
        $cache->destroy('sql', MODERATOR_CACHE_TABLE);
2306
2307
        // Clear table
2308
        switch ($db->sql_layer)
2309
        {
2310
                case 'sqlite':
2311
                case 'firebird':
2312
                        $db->sql_query('DELETE FROM ' . MODERATOR_CACHE_TABLE);
2313
                break;
2314
2315
                default:
2316
                        $db->sql_query('TRUNCATE TABLE ' . MODERATOR_CACHE_TABLE);
2317
                break;
2318
        }
2319
2320
        // We add moderators who have forum moderator permissions without an explicit ACL_NEVER setting
2321
        $hold_ary = $ug_id_ary = $sql_ary = array();
2322
2323
        // Grab all users having moderative options...
2324
        $hold_ary = $auth->acl_user_raw_data(false, 'm_%', false);
2325
2326
        // Add users?
2327
        if (sizeof($hold_ary))
2328
        {
2329
                // At least one moderative option warrants a display
2330
                $ug_id_ary = array_keys($hold_ary);
2331
2332
                // Remove users who have group memberships with DENY moderator permissions
2333
                $sql = $db->sql_build_query('SELECT', array(
2334
                        'SELECT'        => 'a.forum_id, ug.user_id, g.group_id',
2335
2336
                        'FROM'                => array(
2337
                                ACL_OPTIONS_TABLE        => 'o',
2338
                                USER_GROUP_TABLE        => 'ug',
2339
                                GROUPS_TABLE                => 'g',
2340
                                ACL_GROUPS_TABLE        => 'a',
2341
                        ),
2342
2343
                        'LEFT_JOIN'        => array(
2344
                                array(
2345
                                        'FROM'        => array(ACL_ROLES_DATA_TABLE => 'r'),
2346
                                        'ON'        => 'a.auth_role_id = r.role_id'
2347
                                )
2348
                        ),
2349
2350
                        'WHERE'                => '(o.auth_option_id = a.auth_option_id OR o.auth_option_id = r.auth_option_id)
2351
                                AND ((a.auth_setting = ' . ACL_NEVER . ' AND r.auth_setting IS NULL)
2352
                                        OR r.auth_setting = ' . ACL_NEVER . ')
2353
                                AND a.group_id = ug.group_id
2354
                                AND g.group_id = ug.group_id
2355
                                AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1)
2356
                                AND ' . $db->sql_in_set('ug.user_id', $ug_id_ary) . "
2357
                                AND ug.user_pending = 0
2358
                                AND o.auth_option " . $db->sql_like_expression('m_' . $db->any_char),
2359
                ));
2360
                $result = $db->sql_query($sql);
2361
2362
                while ($row = $db->sql_fetchrow($result))
2363
                {
2364
                        if (isset($hold_ary[$row['user_id']][$row['forum_id']]))
2365
                        {
2366
                                unset($hold_ary[$row['user_id']][$row['forum_id']]);
2367
                        }
2368
                }
2369
                $db->sql_freeresult($result);
2370
2371
                if (sizeof($hold_ary))
2372
                {
2373
                        // Get usernames...
2374
                        $sql = 'SELECT user_id, username
2375
                                FROM ' . USERS_TABLE . '
2376
                                WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary));
2377
                        $result = $db->sql_query($sql);
2378
2379
                        $usernames_ary = array();
2380
                        while ($row = $db->sql_fetchrow($result))
2381
                        {
2382
                                $usernames_ary[$row['user_id']] = $row['username'];
2383
                        }
2384
2385
                        foreach ($hold_ary as $user_id => $forum_id_ary)
2386
                        {
2387
                                // Do not continue if user does not exist
2388
                                if (!isset($usernames_ary[$user_id]))
2389
                                {
2390
                                        continue;
2391
                                }
2392
2393
                                foreach ($forum_id_ary as $forum_id => $auth_ary)
2394
                                {
2395
                                        $sql_ary[] = array(
2396
                                                'forum_id'                => (int) $forum_id,
2397
                                                'user_id'                => (int) $user_id,
2398
                                                'username'                => (string) $usernames_ary[$user_id],
2399
                                                'group_id'                => 0,
2400
                                                'group_name'        => ''
2401
                                        );
2402
                                }
2403
                        }
2404
                }
2405
        }
2406
2407
        // Now to the groups...
2408
        $hold_ary = $auth->acl_group_raw_data(false, 'm_%', false);
2409
2410
        if (sizeof($hold_ary))
2411
        {
2412
                $ug_id_ary = array_keys($hold_ary);
2413
2414
                // Make sure not hidden or special groups are involved...
2415
                $sql = 'SELECT group_name, group_id, group_type
2416
                        FROM ' . GROUPS_TABLE . '
2417
                        WHERE ' . $db->sql_in_set('group_id', $ug_id_ary);
2418
                $result = $db->sql_query($sql);
2419
2420
                $groupnames_ary = array();
2421
                while ($row = $db->sql_fetchrow($result))
2422
                {
2423
                        if ($row['group_type'] == GROUP_HIDDEN || $row['group_type'] == GROUP_SPECIAL)
2424
                        {
2425
                                unset($hold_ary[$row['group_id']]);
2426
                        }
2427
2428
                        $groupnames_ary[$row['group_id']] = $row['group_name'];
2429
                }
2430
                $db->sql_freeresult($result);
2431
2432
                foreach ($hold_ary as $group_id => $forum_id_ary)
2433
                {
2434
                        // If there is no group, we do not assign it...
2435
                        if (!isset($groupnames_ary[$group_id]))
2436
                        {
2437
                                continue;
2438
                        }
2439
2440
                        foreach ($forum_id_ary as $forum_id => $auth_ary)
2441
                        {
2442
                                $flag = false;
2443
                                foreach ($auth_ary as $auth_option => $setting)
2444
                                {
2445
                                        // Make sure at least one ACL_YES option is set...
2446
                                        if ($setting == ACL_YES)
2447
                                        {
2448
                                                $flag = true;
2449
                                                break;
2450
                                        }
2451
                                }
2452
2453
                                if (!$flag)
2454
                                {
2455
                                        continue;
2456
                                }
2457
2458
                                $sql_ary[] = array(
2459
                                        'forum_id'                => (int) $forum_id,
2460
                                        'user_id'                => 0,
2461
                                        'username'                => '',
2462
                                        'group_id'                => (int) $group_id,
2463
                                        'group_name'        => (string) $groupnames_ary[$group_id]
2464
                                );
2465
                        }
2466
                }
2467
        }
2468
2469
        $db->sql_multi_insert(MODERATOR_CACHE_TABLE, $sql_ary);
2470
}
2471
2472
/**
2473
* View log
2474
* If $log_count is set to false, we will skip counting all entries in the database.
2475
*/
2476
function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $limit_days = 0, $sort_by = 'l.log_time DESC', $keywords = '')
2477
{
2478
        global $db, $user, $auth, $phpEx, $phpbb_root_path, $phpbb_admin_path;
2479
2480
        $topic_id_list = $reportee_id_list = $is_auth = $is_mod = array();
2481
2482
        $profile_url = (defined('IN_ADMIN')) ? append_sid("{$phpbb_admin_path}index.$phpEx", 'i=users&amp;mode=overview') : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile');
2483
2484
        switch ($mode)
2485
        {
2486
                case 'admin':
2487
                        $log_type = LOG_ADMIN;
2488
                        $sql_forum = '';
2489
                break;
2490
2491
                case 'mod':
2492
                        $log_type = LOG_MOD;
2493
                        $sql_forum = '';
2494
2495
                        if ($topic_id)
2496
                        {
2497
                                $sql_forum = 'AND l.topic_id = ' . (int) $topic_id;
2498
                        }
2499
                        else if (is_array($forum_id))
2500
                        {
2501
                                $sql_forum = 'AND ' . $db->sql_in_set('l.forum_id', array_map('intval', $forum_id));
2502
                        }
2503
                        else if ($forum_id)
2504
                        {
2505
                                $sql_forum = 'AND l.forum_id = ' . (int) $forum_id;
2506
                        }
2507
                break;
2508
2509
                case 'user':
2510
                        $log_type = LOG_USERS;
2511
                        $sql_forum = 'AND l.reportee_id = ' . (int) $user_id;
2512
                break;
2513
2514
                case 'users':
2515
                        $log_type = LOG_USERS;
2516
                        $sql_forum = '';
2517
                break;
2518
2519
                case 'critical':
2520
                        $log_type = LOG_CRITICAL;
2521
                        $sql_forum = '';
2522
                break;
2523
2524
                default:
2525
                        return;
2526
        }
2527
2528
        // Use no preg_quote for $keywords because this would lead to sole backslashes being added
2529
        // We also use an OR connection here for spaces and the | string. Currently, regex is not supported for searching (but may come later).
2530
        $keywords = preg_split('#[\s|]+#u', utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY);
2531
        $sql_keywords = '';
2532
2533
        if (!empty($keywords))
2534
        {
2535
                $keywords_pattern = array();
2536
2537
                // Build pattern and keywords...
2538
                for ($i = 0, $num_keywords = sizeof($keywords); $i < $num_keywords; $i++)
2539
                {
2540
                        $keywords_pattern[] = preg_quote($keywords[$i], '#');
2541
                        $keywords[$i] = $db->sql_like_expression($db->any_char . $keywords[$i] . $db->any_char);
2542
                }
2543
2544
                $keywords_pattern = '#' . implode('|', $keywords_pattern) . '#ui';
2545
2546
                $operations = array();
2547
                foreach ($user->lang as $key => $value)
2548
                {
2549
                        if (substr($key, 0, 4) == 'LOG_' && preg_match($keywords_pattern, $value))
2550
                        {
2551
                                $operations[] = $key;
2552
                        }
2553
                }
2554
2555
                $sql_keywords = 'AND (';
2556
                if (!empty($operations))
2557
                {
2558
                        $sql_keywords .= $db->sql_in_set('l.log_operation', $operations) . ' OR ';
2559
                }
2560
                $sql_keywords .= 'LOWER(l.log_data) ' . implode(' OR LOWER(l.log_data) ', $keywords) . ')';
2561
        }
2562
2563
        if ($log_count !== false)
2564
        {
2565
                $sql = 'SELECT COUNT(l.log_id) AS total_entries
2566
                        FROM ' . LOG_TABLE . ' l, ' . USERS_TABLE . " u
2567
                        WHERE l.log_type = $log_type
2568
                                AND l.user_id = u.user_id
2569
                                AND l.log_time >= $limit_days
2570
                                $sql_keywords
2571
                                $sql_forum";
2572
                $result = $db->sql_query($sql);
2573
                $log_count = (int) $db->sql_fetchfield('total_entries');
2574
                $db->sql_freeresult($result);
2575
        }
2576
2577
        // $log_count may be false here if false was passed in for it,
2578
        // because in this case we did not run the COUNT() query above.
2579
        // If we ran the COUNT() query and it returned zero rows, return;
2580
        // otherwise query for logs below.
2581
        if ($log_count === 0)
2582
        {
2583
                // Save the queries, because there are no logs to display
2584
                return 0;
2585
        }
2586
2587
        if ($offset >= $log_count)
2588
        {
2589
                $offset = ($offset - $limit < 0) ? 0 : $offset - $limit;
2590
        }
2591
2592
        $sql = "SELECT l.*, u.username, u.username_clean, u.user_colour
2593
                FROM " . LOG_TABLE . " l, " . USERS_TABLE . " u
2594
                WHERE l.log_type = $log_type
2595
                        AND u.user_id = l.user_id
2596
                        " . (($limit_days) ? "AND l.log_time >= $limit_days" : '') . "
2597
                        $sql_keywords
2598
                        $sql_forum
2599
                ORDER BY $sort_by";
2600
        $result = $db->sql_query_limit($sql, $limit, $offset);
2601
2602
        $i = 0;
2603
        $log = array();
2604
        while ($row = $db->sql_fetchrow($result))
2605
        {
2606
                if ($row['topic_id'])
2607
                {
2608
                        $topic_id_list[] = $row['topic_id'];
2609
                }
2610
2611
                if ($row['reportee_id'])
2612
                {
2613
                        $reportee_id_list[] = $row['reportee_id'];
2614
                }
2615
2616
                $log[$i] = array(
2617
                        'id'                                => $row['log_id'],
2618
2619
                        'reportee_id'                        => $row['reportee_id'],
2620
                        'reportee_username'                => '',
2621
                        'reportee_username_full'=> '',
2622
2623
                        'user_id'                        => $row['user_id'],
2624
                        'username'                        => $row['username'],
2625
                        'username_full'                => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], false, $profile_url),
2626
2627
                        'ip'                                => $row['log_ip'],
2628
                        'time'                                => $row['log_time'],
2629
                        'forum_id'                        => $row['forum_id'],
2630
                        'topic_id'                        => $row['topic_id'],
2631
2632
                        'viewforum'                        => ($row['forum_id'] && $auth->acl_get('f_read', $row['forum_id'])) ? append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $row['forum_id']) : false,
2633
                        'action'                        => (isset($user->lang[$row['log_operation']])) ? $user->lang[$row['log_operation']] : '{' . ucfirst(str_replace('_', ' ', $row['log_operation'])) . '}',
2634
                );
2635
2636
                if (!empty($row['log_data']))
2637
                {
2638
                        $log_data_ary = @unserialize($row['log_data']);
2639
                        $log_data_ary = ($log_data_ary === false) ? array() : $log_data_ary;
2640
2641
                        if (isset($user->lang[$row['log_operation']]))
2642
                        {
2643
                                // Check if there are more occurrences of % than arguments, if there are we fill out the arguments array
2644
                                // It doesn't matter if we add more arguments than placeholders
2645
                                if ((substr_count($log[$i]['action'], '%') - sizeof($log_data_ary)) > 0)
2646
                                {
2647
                                        $log_data_ary = array_merge($log_data_ary, array_fill(0, substr_count($log[$i]['action'], '%') - sizeof($log_data_ary), ''));
2648
                                }
2649
2650
                                $log[$i]['action'] = vsprintf($log[$i]['action'], $log_data_ary);
2651
2652
                                // If within the admin panel we do not censor text out
2653
                                if (defined('IN_ADMIN'))
2654
                                {
2655
                                        $log[$i]['action'] = bbcode_nl2br($log[$i]['action']);
2656
                                }
2657
                                else
2658
                                {
2659
                                        $log[$i]['action'] = bbcode_nl2br(censor_text($log[$i]['action']));
2660
                                }
2661
                        }
2662
                        else if (!empty($log_data_ary))
2663
                        {
2664
                                $log[$i]['action'] .= '<br />' . implode('', $log_data_ary);
2665
                        }
2666
2667
                        /* Apply make_clickable... has to be seen if it is for good. :/
2668
                        // Seems to be not for the moment, reconsider later...
2669
                        $log[$i]['action'] = make_clickable($log[$i]['action']);
2670
                        */
2671
                }
2672
2673
                $i++;
2674
        }
2675
        $db->sql_freeresult($result);
2676
2677
        if (sizeof($topic_id_list))
2678
        {
2679
                $topic_id_list = array_unique($topic_id_list);
2680
2681
                // This query is not really needed if move_topics() updates the forum_id field,
2682
                // although it's also used to determine if the topic still exists in the database
2683
                $sql = 'SELECT topic_id, forum_id
2684
                        FROM ' . TOPICS_TABLE . '
2685
                        WHERE ' . $db->sql_in_set('topic_id', array_map('intval', $topic_id_list));
2686
                $result = $db->sql_query($sql);
2687
2688
                $default_forum_id = 0;
2689
2690
                while ($row = $db->sql_fetchrow($result))
2691
                {
2692
                        if (!$row['forum_id'])
2693
                        {
2694
                                if ($auth->acl_getf_global('f_read'))
2695
                                {
2696
                                        if (!$default_forum_id)
2697
                                        {
2698
                                                $sql = 'SELECT forum_id
2699
                                                        FROM ' . FORUMS_TABLE . '
2700
                                                        WHERE forum_type = ' . FORUM_POST;
2701
                                                $f_result = $db->sql_query_limit($sql, 1);
2702
                                                $default_forum_id = (int) $db->sql_fetchfield('forum_id', false, $f_result);
2703
                                                $db->sql_freeresult($f_result);
2704
                                        }
2705
2706
                                        $is_auth[$row['topic_id']] = $default_forum_id;
2707
                                }
2708
                        }
2709
                        else
2710
                        {
2711
                                if ($auth->acl_get('f_read', $row['forum_id']))
2712
                                {
2713
                                        $is_auth[$row['topic_id']] = $row['forum_id'];
2714
                                }
2715
                        }
2716
2717
                        if ($auth->acl_gets('a_', 'm_', $row['forum_id']))
2718
                        {
2719
                                $is_mod[$row['topic_id']] = $row['forum_id'];
2720
                        }
2721
                }
2722
                $db->sql_freeresult($result);
2723
2724
                foreach ($log as $key => $row)
2725
                {
2726
                        $log[$key]['viewtopic'] = (isset($is_auth[$row['topic_id']])) ? append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'f=' . $is_auth[$row['topic_id']] . '&amp;t=' . $row['topic_id']) : false;
2727
                        $log[$key]['viewlogs'] = (isset($is_mod[$row['topic_id']])) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=logs&amp;mode=topic_logs&amp;t=' . $row['topic_id'], true, $user->session_id) : false;
2728
                }
2729
        }
2730
2731
        if (sizeof($reportee_id_list))
2732
        {
2733
                $reportee_id_list = array_unique($reportee_id_list);
2734
                $reportee_names_list = array();
2735
2736
                $sql = 'SELECT user_id, username, user_colour
2737
                        FROM ' . USERS_TABLE . '
2738
                        WHERE ' . $db->sql_in_set('user_id', $reportee_id_list);
2739
                $result = $db->sql_query($sql);
2740
2741
                while ($row = $db->sql_fetchrow($result))
2742
                {
2743
                        $reportee_names_list[$row['user_id']] = $row;
2744
                }
2745
                $db->sql_freeresult($result);
2746
2747
                foreach ($log as $key => $row)
2748
                {
2749
                        if (!isset($reportee_names_list[$row['reportee_id']]))
2750
                        {
2751
                                continue;
2752
                        }
2753
2754
                        $log[$key]['reportee_username'] = $reportee_names_list[$row['reportee_id']]['username'];
2755
                        $log[$key]['reportee_username_full'] = get_username_string('full', $row['reportee_id'], $reportee_names_list[$row['reportee_id']]['username'], $reportee_names_list[$row['reportee_id']]['user_colour'], false, $profile_url);
2756
                }
2757
        }
2758
2759
        return $offset;
2760
}
2761
2762
/**
2763
* Update foes - remove moderators and administrators from foe lists...
2764
*/
2765
function update_foes($group_id = false, $user_id = false)
2766
{
2767
        global $db, $auth;
2768
2769
        // update foes for some user
2770
        if (is_array($user_id) && sizeof($user_id))
2771
        {
2772
                $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
2773
                        WHERE ' . $db->sql_in_set('zebra_id', $user_id) . '
2774
                                AND foe = 1';
2775
                $db->sql_query($sql);
2776
                return;
2777
        }
2778
2779
        // update foes for some group
2780
        if (is_array($group_id) && sizeof($group_id))
2781
        {
2782
                // Grab group settings...
2783
                $sql = $db->sql_build_query('SELECT', array(
2784
                        'SELECT'        => 'a.group_id',
2785
2786
                        'FROM'                => array(
2787
                                ACL_OPTIONS_TABLE        => 'ao',
2788
                                ACL_GROUPS_TABLE        => 'a'
2789
                        ),
2790
2791
                        'LEFT_JOIN'        => array(
2792
                                array(
2793
                                        'FROM'        => array(ACL_ROLES_DATA_TABLE => 'r'),
2794
                                        'ON'        => 'a.auth_role_id = r.role_id'
2795
                                ),
2796
                        ),
2797
2798
                        'WHERE'                => '(ao.auth_option_id = a.auth_option_id OR ao.auth_option_id = r.auth_option_id)
2799
                                AND ' . $db->sql_in_set('a.group_id', $group_id) . "
2800
                                AND ao.auth_option IN ('a_', 'm_')",
2801
2802
                        'GROUP_BY'        => 'a.group_id'
2803
                ));
2804
                $result = $db->sql_query($sql);
2805
2806
                $groups = array();
2807
                while ($row = $db->sql_fetchrow($result))
2808
                {
2809
                        $groups[] = (int) $row['group_id'];
2810
                }
2811
                $db->sql_freeresult($result);
2812
2813
                if (!sizeof($groups))
2814
                {
2815
                        return;
2816
                }
2817
2818
                switch ($db->sql_layer)
2819
                {
2820
                        case 'mysqli':
2821
                        case 'mysql4':
2822
                                $sql = 'DELETE ' . (($db->sql_layer === 'mysqli' || version_compare($db->sql_server_info(true), '4.1', '>=')) ? 'z.*' : ZEBRA_TABLE) . '
2823
                                        FROM ' . ZEBRA_TABLE . ' z, ' . USER_GROUP_TABLE . ' ug
2824
                                        WHERE z.zebra_id = ug.user_id
2825
                                                AND z.foe = 1
2826
                                                AND ' . $db->sql_in_set('ug.group_id', $groups);
2827
                                $db->sql_query($sql);
2828
                        break;
2829
2830
                        default:
2831
                                $sql = 'SELECT user_id
2832
                                        FROM ' . USER_GROUP_TABLE . '
2833
                                        WHERE ' . $db->sql_in_set('group_id', $groups);
2834
                                $result = $db->sql_query($sql);
2835
2836
                                $users = array();
2837
                                while ($row = $db->sql_fetchrow($result))
2838
                                {
2839
                                        $users[] = (int) $row['user_id'];
2840
                                }
2841
                                $db->sql_freeresult($result);
2842
2843
                                if (sizeof($users))
2844
                                {
2845
                                        $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
2846
                                                WHERE ' . $db->sql_in_set('zebra_id', $users) . '
2847
                                                        AND foe = 1';
2848
                                        $db->sql_query($sql);
2849
                                }
2850
                        break;
2851
                }
2852
2853
                return;
2854
        }
2855
2856
        // update foes for everyone
2857
        $perms = array();
2858
        foreach ($auth->acl_get_list(false, array('a_', 'm_'), false) as $forum_id => $forum_ary)
2859
        {
2860
                foreach ($forum_ary as $auth_option => $user_ary)
2861
                {
2862
                        $perms = array_merge($perms, $user_ary);
2863
                }
2864
        }
2865
2866
        if (sizeof($perms))
2867
        {
2868
                $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
2869
                        WHERE ' . $db->sql_in_set('zebra_id', array_unique($perms)) . '
2870
                                AND foe = 1';
2871
                $db->sql_query($sql);
2872
        }
2873
        unset($perms);
2874
}
2875
2876
/**
2877
* Lists inactive users
2878
*/
2879
function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC')
2880
{
2881
        global $db, $user;
2882
2883
        $sql = 'SELECT COUNT(user_id) AS user_count
2884
                FROM ' . USERS_TABLE . '
2885
                WHERE user_type = ' . USER_INACTIVE .
2886
                (($limit_days) ? " AND user_inactive_time >= $limit_days" : '');
2887
        $result = $db->sql_query($sql);
2888
        $user_count = (int) $db->sql_fetchfield('user_count');
2889
        $db->sql_freeresult($result);
2890
2891
        if ($user_count == 0)
2892
        {
2893
                // Save the queries, because there are no users to display
2894
                return 0;
2895
        }
2896
2897
        if ($offset >= $user_count)
2898
        {
2899
                $offset = ($offset - $limit < 0) ? 0 : $offset - $limit;
2900
        }
2901
2902
        $sql = 'SELECT *
2903
                FROM ' . USERS_TABLE . '
2904
                WHERE user_type = ' . USER_INACTIVE .
2905
                (($limit_days) ? " AND user_inactive_time >= $limit_days" : '') . "
2906
                ORDER BY $sort_by";
2907
        $result = $db->sql_query_limit($sql, $limit, $offset);
2908
2909
        while ($row = $db->sql_fetchrow($result))
2910
        {
2911
                $row['inactive_reason'] = $user->lang['INACTIVE_REASON_UNKNOWN'];
2912
                switch ($row['user_inactive_reason'])
2913
                {
2914
                        case INACTIVE_REGISTER:
2915
                                $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REGISTER'];
2916
                        break;
2917
2918
                        case INACTIVE_PROFILE:
2919
                                $row['inactive_reason'] = $user->lang['INACTIVE_REASON_PROFILE'];
2920
                        break;
2921
2922
                        case INACTIVE_MANUAL:
2923
                                $row['inactive_reason'] = $user->lang['INACTIVE_REASON_MANUAL'];
2924
                        break;
2925
2926
                        case INACTIVE_REMIND:
2927
                                $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REMIND'];
2928
                        break;
2929
                }
2930
2931
                $users[] = $row;
2932
        }
2933
2934
        return $offset;
2935
}
2936
2937
/**
2938
* Lists warned users
2939
*/
2940
function view_warned_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_warnings DESC')
2941
{
2942
        global $db;
2943
2944
        $sql = 'SELECT user_id, username, user_colour, user_warnings, user_last_warning
2945
                FROM ' . USERS_TABLE . '
2946
                WHERE user_warnings > 0
2947
                ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '') . "
2948
                ORDER BY $sort_by";
2949
        $result = $db->sql_query_limit($sql, $limit, $offset);
2950
        $users = $db->sql_fetchrowset($result);
2951
        $db->sql_freeresult($result);
2952
2953
        $sql = 'SELECT count(user_id) AS user_count
2954
                FROM ' . USERS_TABLE . '
2955
                WHERE user_warnings > 0
2956
                ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '');
2957
        $result = $db->sql_query($sql);
2958
        $user_count = (int) $db->sql_fetchfield('user_count');
2959
        $db->sql_freeresult($result);
2960
2961
        return;
2962
}
2963
2964
/**
2965
* Get database size
2966
* Currently only mysql and mssql are supported
2967
*/
2968
function get_database_size()
2969
{
2970
        global $db, $user, $table_prefix;
2971
2972
        $database_size = false;
2973
2974
        // This code is heavily influenced by a similar routine in phpMyAdmin 2.2.0
2975
        switch ($db->sql_layer)
2976
        {
2977
                case 'mysql':
2978
                case 'mysql4':
2979
                case 'mysqli':
2980
                        $sql = 'SELECT VERSION() AS mysql_version';
2981
                        $result = $db->sql_query($sql);
2982
                        $row = $db->sql_fetchrow($result);
2983
                        $db->sql_freeresult($result);
2984
2985
                        if ($row)
2986
                        {
2987
                                $version = $row['mysql_version'];
2988
2989
                                if (preg_match('#(3\.23|[45]\.)#', $version))
2990
                                {
2991
                                        $db_name = (preg_match('#^(?:3\.23\.(?:[6-9]|[1-9]{2}))|[45]\.#', $version)) ? "`{$db->dbname}`" : $db->dbname;
2992
2993
                                        $sql = 'SHOW TABLE STATUS
2994
                                                FROM ' . $db_name;
2995
                                        $result = $db->sql_query($sql, 7200);
2996
2997
                                        $database_size = 0;
2998
                                        while ($row = $db->sql_fetchrow($result))
2999
                                        {
3000
                                                if ((isset($row['Type']) && $row['Type'] != 'MRG_MyISAM') || (isset($row['Engine']) && ($row['Engine'] == 'MyISAM' || $row['Engine'] == 'InnoDB')))
3001
                                                {
3002
                                                        if ($table_prefix != '')
3003
                                                        {
3004
                                                                if (strpos($row['Name'], $table_prefix) !== false)
3005
                                                                {
3006
                                                                        $database_size += $row['Data_length'] + $row['Index_length'];
3007
                                                                }
3008
                                                        }
3009
                                                        else
3010
                                                        {
3011
                                                                $database_size += $row['Data_length'] + $row['Index_length'];
3012
                                                        }
3013
                                                }
3014
                                        }
3015
                                        $db->sql_freeresult($result);
3016
                                }
3017
                        }
3018
                break;
3019
3020
                case 'firebird':
3021
                        global $dbname;
3022
3023
                        // if it on the local machine, we can get lucky
3024
                        if (file_exists($dbname))
3025
                        {
3026
                                $database_size = filesize($dbname);
3027
                        }
3028
3029
                break;
3030
3031
                case 'sqlite':
3032
                        global $dbhost;
3033
3034
                        if (file_exists($dbhost))
3035
                        {
3036
                                $database_size = filesize($dbhost);
3037
                        }
3038
3039
                break;
3040
3041
                case 'mssql':
3042
                case 'mssql_odbc':
3043
                case 'mssqlnative':
3044
                        $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize
3045
                                FROM sysfiles';
3046
                        $result = $db->sql_query($sql, 7200);
3047
                        $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false;
3048
                        $db->sql_freeresult($result);
3049
                break;
3050
3051
                case 'postgres':
3052
                        $sql = "SELECT proname
3053
                                FROM pg_proc
3054
                                WHERE proname = 'pg_database_size'";
3055
                        $result = $db->sql_query($sql);
3056
                        $row = $db->sql_fetchrow($result);
3057
                        $db->sql_freeresult($result);
3058
3059
                        if ($row['proname'] == 'pg_database_size')
3060
                        {
3061
                                $database = $db->dbname;
3062
                                if (strpos($database, '.') !== false)
3063
                                {
3064
                                        list($database, ) = explode('.', $database);
3065
                                }
3066
3067
                                $sql = "SELECT oid
3068
                                        FROM pg_database
3069
                                        WHERE datname = '$database'";
3070
                                $result = $db->sql_query($sql);
3071
                                $row = $db->sql_fetchrow($result);
3072
                                $db->sql_freeresult($result);
3073
3074
                                $oid = $row['oid'];
3075
3076
                                $sql = 'SELECT pg_database_size(' . $oid . ') as size';
3077
                                $result = $db->sql_query($sql);
3078
                                $row = $db->sql_fetchrow($result);
3079
                                $db->sql_freeresult($result);
3080
3081
                                $database_size = $row['size'];
3082
                        }
3083
                break;
3084
3085
                case 'oracle':
3086
                        $sql = 'SELECT SUM(bytes) as dbsize
3087
                                FROM user_segments';
3088
                        $result = $db->sql_query($sql, 7200);
3089
                        $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false;
3090
                        $db->sql_freeresult($result);
3091
                break;
3092
        }
3093
3094
        $database_size = ($database_size !== false) ? get_formatted_filesize($database_size) : $user->lang['NOT_AVAILABLE'];
3095
3096
        return $database_size;
3097
}
3098
3099
/**
3100
* Retrieve contents from remotely stored file
3101
*/
3102
function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port = 80, $timeout = 6)
3103
{
3104
        global $user;
3105
3106
        if ($fsock = @fsockopen($host, $port, $errno, $errstr, $timeout))
3107
        {
3108
                @fputs($fsock, "GET $directory/$filename HTTP/1.1\r\n");
3109
                @fputs($fsock, "HOST: $host\r\n");
3110
                @fputs($fsock, "Connection: close\r\n\r\n");
3111
3112
                $timer_stop = time() + $timeout;
3113
                stream_set_timeout($fsock, $timeout);
3114
3115
                $file_info = '';
3116
                $get_info = false;
3117
3118
                while (!@feof($fsock))
3119
                {
3120
                        if ($get_info)
3121
                        {
3122
                                $file_info .= @fread($fsock, 1024);
3123
                        }
3124
                        else
3125
                        {
3126
                                $line = @fgets($fsock, 1024);
3127
                                if ($line == "\r\n")
3128
                                {
3129
                                        $get_info = true;
3130
                                }
3131
                                else if (stripos($line, '404 not found') !== false)
3132
                                {
3133
                                        $errstr = $user->lang['FILE_NOT_FOUND'] . ': ' . $filename;
3134
                                        return false;
3135
                                }
3136
                        }
3137
3138
                        $stream_meta_data = stream_get_meta_data($fsock);
3139
3140
                        if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop)
3141
                        {
3142
                                $errstr = $user->lang['FSOCK_TIMEOUT'];
3143
                                return false;
3144
                        }
3145
                }
3146
                @fclose($fsock);
3147
        }
3148
        else
3149
        {
3150
                if ($errstr)
3151
                {
3152
                        $errstr = utf8_convert_message($errstr);
3153
                        return false;
3154
                }
3155
                else
3156
                {
3157
                        $errstr = $user->lang['FSOCK_DISABLED'];
3158
                        return false;
3159
                }
3160
        }
3161
3162
        return $file_info;
3163
}
3164
3165
/**
3166
* Tidy Warnings
3167
* Remove all warnings which have now expired from the database
3168
* The duration of a warning can be defined by the administrator
3169
* This only removes the warning and reduces the associated count,
3170
* it does not remove the user note recording the contents of the warning
3171
*/
3172
function tidy_warnings()
3173
{
3174
        global $db, $config;
3175
3176
        $expire_date = time() - ($config['warnings_expire_days'] * 86400);
3177
        $warning_list = $user_list = array();
3178
3179
        $sql = 'SELECT * FROM ' . WARNINGS_TABLE . "
3180
                WHERE warning_time < $expire_date";
3181
        $result = $db->sql_query($sql);
3182
3183
        while ($row = $db->sql_fetchrow($result))
3184
        {
3185
                $warning_list[] = $row['warning_id'];
3186
                $user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1;
3187
        }
3188
        $db->sql_freeresult($result);
3189
3190
        if (sizeof($warning_list))
3191
        {
3192
                $db->sql_transaction('begin');
3193
3194
                $sql = 'DELETE FROM ' . WARNINGS_TABLE . '
3195
                        WHERE ' . $db->sql_in_set('warning_id', $warning_list);
3196
                $db->sql_query($sql);
3197
3198
                foreach ($user_list as $user_id => $value)
3199
                {
3200
                        $sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value
3201
                                WHERE user_id = $user_id";
3202
                        $db->sql_query($sql);
3203
                }
3204
3205
                $db->sql_transaction('commit');
3206
        }
3207
3208
        set_config('warnings_last_gc', time(), true);
3209
}
3210
3211
/**
3212
* Tidy database, doing some maintanance tasks
3213
*/
3214
function tidy_database()
3215
{
3216
        global $db;
3217
3218
        // Here we check permission consistency
3219
3220
        // Sometimes, it can happen permission tables having forums listed which do not exist
3221
        $sql = 'SELECT forum_id
3222
                FROM ' . FORUMS_TABLE;
3223
        $result = $db->sql_query($sql);
3224
3225
        $forum_ids = array(0);
3226
        while ($row = $db->sql_fetchrow($result))
3227
        {
3228
                $forum_ids[] = $row['forum_id'];
3229
        }
3230
        $db->sql_freeresult($result);
3231
3232
        // Delete those rows from the acl tables not having listed the forums above
3233
        $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
3234
                WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
3235
        $db->sql_query($sql);
3236
3237
        $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
3238
                WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
3239
        $db->sql_query($sql);
3240
3241
        set_config('database_last_gc', time(), true);
3242
}
3243
3244
/**
3245
* Add permission language - this will make sure custom files will be included
3246
*/
3247
function add_permission_language()
3248
{
3249
        global $user, $phpEx;
3250
3251
        // First of all, our own file. We need to include it as the first file because it presets all relevant variables.
3252
        $user->add_lang('acp/permissions_phpbb');
3253
3254
        $files_to_add = array();
3255
3256
        // Now search in acp and mods folder for permissions_ files.
3257
        foreach (array('acp/', 'mods/') as $path)
3258
        {
3259
                $dh = @opendir($user->lang_path . $user->lang_name . '/' . $path);
3260
3261
                if ($dh)
3262
                {
3263
                        while (($file = readdir($dh)) !== false)
3264
                        {
3265
                                if ($file !== 'permissions_phpbb.' . $phpEx && strpos($file, 'permissions_') === 0 && substr($file, -(strlen($phpEx) + 1)) === '.' . $phpEx)
3266
                                {
3267
                                        $files_to_add[] = $path . substr($file, 0, -(strlen($phpEx) + 1));
3268
                                }
3269
                        }
3270
                        closedir($dh);
3271
                }
3272
        }
3273
3274
        if (!sizeof($files_to_add))
3275
        {
3276
                return false;
3277
        }
3278
3279
        $user->add_lang($files_to_add);
3280
        return true;
3281
}
3282
3283
/**
3284
 * Obtains the latest version information
3285
 *
3286
 * @param bool $force_update Ignores cached data. Defaults to false.
3287
 * @param bool $warn_fail Trigger a warning if obtaining the latest version information fails. Defaults to false.
3288
 * @param int $ttl Cache version information for $ttl seconds. Defaults to 86400 (24 hours).
3289
 *
3290
 * @return string | false Version info on success, false on failure.
3291
 */
3292
function obtain_latest_version_info($force_update = false, $warn_fail = false, $ttl = 86400)
3293
{
3294
        global $cache;
3295
3296
        $info = $cache->get('versioncheck');
3297
3298
        if ($info === false || $force_update)
3299
        {
3300
                $errstr = '';
3301
                $errno = 0;
3302
3303
                $info = get_remote_file('version.phpbb.com', '/phpbb',
3304
                                ((defined('PHPBB_QA')) ? '30x_qa.txt' : '30x.txt'), $errstr, $errno);
3305
3306
                if ($info === false)
3307
                {
3308
                        $cache->destroy('versioncheck');
3309
                        if ($warn_fail)
3310
                        {
3311
                                trigger_error($errstr, E_USER_WARNING);
3312
                        }
3313
                        return false;
3314
                }
3315
3316
                $cache->put('versioncheck', $info, $ttl);
3317
        }
3318
3319
        return $info;
3320
}
3321
3322
/**
3323
 * Enables a particular flag in a bitfield column of a given table.
3324
 *
3325
 * @param string        $table_name                The table to update
3326
 * @param string        $column_name        The column containing a bitfield to update
3327
 * @param int                $flag                        The binary flag which is OR-ed with the current column value
3328
 * @param string        $sql_more                This string is attached to the sql query generated to update the table.
3329
 *
3330
 * @return void
3331
 */
3332
function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '')
3333
{
3334
        global $db;
3335
3336
        $sql = 'UPDATE ' . $table_name . '
3337
                SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . '
3338
                ' . $sql_more;
3339
        $db->sql_query($sql);
3340
}
3341
3342
?>