phpBB
Statistics
| Revision:

root / trunk / phpBB / includes / functions_admin.php

History | View | Annotate | Download (88 kB)

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