phpBB
Statistics
| Revision:

root / trunk / phpBB / includes / functions_content.php

History | View | Annotate | Download (37.4 kB)

1
<?php
2
/**
3
*
4
* @package phpBB3
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
* gen_sort_selects()
20
* make_jumpbox()
21
* bump_topic_allowed()
22
* get_context()
23
* decode_message()
24
* strip_bbcode()
25
* generate_text_for_display()
26
* generate_text_for_storage()
27
* generate_text_for_edit()
28
* make_clickable_callback()
29
* make_clickable()
30
* censor_text()
31
* bbcode_nl2br()
32
* smiley_text()
33
* parse_attachments()
34
* extension_allowed()
35
* truncate_string()
36
* get_username_string()
37
* class bitfield
38
*/
39
40
/**
41
* Generate sort selection fields
42
*/
43
function gen_sort_selects(&$limit_days, &$sort_by_text, &$sort_days, &$sort_key, &$sort_dir, &$s_limit_days, &$s_sort_key, &$s_sort_dir, &$u_sort_param, $def_st = false, $def_sk = false, $def_sd = false)
44
{
45
        global $user;
46
47
        $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
48
49
        $sorts = array(
50
                'st'        => array(
51
                        'key'                => 'sort_days',
52
                        'default'        => $def_st,
53
                        'options'        => $limit_days,
54
                        'output'        => &$s_limit_days,
55
                ),
56
57
                'sk'        => array(
58
                        'key'                => 'sort_key',
59
                        'default'        => $def_sk,
60
                        'options'        => $sort_by_text,
61
                        'output'        => &$s_sort_key,
62
                ),
63
64
                'sd'        => array(
65
                        'key'                => 'sort_dir',
66
                        'default'        => $def_sd,
67
                        'options'        => $sort_dir_text,
68
                        'output'        => &$s_sort_dir,
69
                ),
70
        );
71
        $u_sort_param  = '';
72
73
        foreach ($sorts as $name => $sort_ary)
74
        {
75
                $key = $sort_ary['key'];
76
                $selected = $$sort_ary['key'];
77
78
                // Check if the key is selectable. If not, we reset to the default or first key found.
79
                // This ensures the values are always valid. We also set $sort_dir/sort_key/etc. to the
80
                // correct value, else the protection is void. ;)
81
                if (!isset($sort_ary['options'][$selected]))
82
                {
83
                        if ($sort_ary['default'] !== false)
84
                        {
85
                                $selected = $$key = $sort_ary['default'];
86
                        }
87
                        else
88
                        {
89
                                @reset($sort_ary['options']);
90
                                $selected = $$key = key($sort_ary['options']);
91
                        }
92
                }
93
94
                $sort_ary['output'] = '<select name="' . $name . '" id="' . $name . '">';
95
                foreach ($sort_ary['options'] as $option => $text)
96
                {
97
                        $sort_ary['output'] .= '<option value="' . $option . '"' . (($selected == $option) ? ' selected="selected"' : '') . '>' . $text . '</option>';
98
                }
99
                $sort_ary['output'] .= '</select>';
100
101
                $u_sort_param .= ($selected !== $sort_ary['default']) ? ((strlen($u_sort_param)) ? '&amp;' : '') . "{$name}={$selected}" : '';
102
        }
103
104
        return;
105
}
106
107
/**
108
* Generate Jumpbox
109
*/
110
function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false, $force_display = false)
111
{
112
        global $config, $auth, $template, $user, $db;
113
114
        // We only return if the jumpbox is not forced to be displayed (in case it is needed for functionality)
115
        if (!$config['load_jumpbox'] && $force_display === false)
116
        {
117
                return;
118
        }
119
120
        $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
121
                FROM ' . FORUMS_TABLE . '
122
                ORDER BY left_id ASC';
123
        $result = $db->sql_query($sql, 600);
124
125
        $right = $padding = 0;
126
        $padding_store = array('0' => 0);
127
        $display_jumpbox = false;
128
        $iteration = 0;
129
130
        // Sometimes it could happen that forums will be displayed here not be displayed within the index page
131
        // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
132
        // If this happens, the padding could be "broken"
133
134
        while ($row = $db->sql_fetchrow($result))
135
        {
136
                if ($row['left_id'] < $right)
137
                {
138
                        $padding++;
139
                        $padding_store[$row['parent_id']] = $padding;
140
                }
141
                else if ($row['left_id'] > $right + 1)
142
                {
143
                        // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
144
                        // @todo digging deep to find out "how" this can happen.
145
                        $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
146
                }
147
148
                $right = $row['right_id'];
149
150
                if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
151
                {
152
                        // Non-postable forum with no subforums, don't display
153
                        continue;
154
                }
155
156
                if (!$auth->acl_get('f_list', $row['forum_id']))
157
                {
158
                        // if the user does not have permissions to list this forum skip
159
                        continue;
160
                }
161
162
                if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
163
                {
164
                        continue;
165
                }
166
167
                if (!$display_jumpbox)
168
                {
169
                        $template->assign_block_vars('jumpbox_forums', array(
170
                                'FORUM_ID'                => ($select_all) ? 0 : -1,
171
                                'FORUM_NAME'        => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
172
                                'S_FORUM_COUNT'        => $iteration)
173
                        );
174
175
                        $iteration++;
176
                        $display_jumpbox = true;
177
                }
178
179
                $template->assign_block_vars('jumpbox_forums', array(
180
                        'FORUM_ID'                => $row['forum_id'],
181
                        'FORUM_NAME'        => $row['forum_name'],
182
                        'SELECTED'                => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
183
                        'S_FORUM_COUNT'        => $iteration,
184
                        'S_IS_CAT'                => ($row['forum_type'] == FORUM_CAT) ? true : false,
185
                        'S_IS_LINK'                => ($row['forum_type'] == FORUM_LINK) ? true : false,
186
                        'S_IS_POST'                => ($row['forum_type'] == FORUM_POST) ? true : false)
187
                );
188
189
                for ($i = 0; $i < $padding; $i++)
190
                {
191
                        $template->assign_block_vars('jumpbox_forums.level', array());
192
                }
193
                $iteration++;
194
        }
195
        $db->sql_freeresult($result);
196
        unset($padding_store);
197
198
        $template->assign_vars(array(
199
                'S_DISPLAY_JUMPBOX'        => $display_jumpbox,
200
                'S_JUMPBOX_ACTION'        => $action)
201
        );
202
203
        return;
204
}
205
206
/**
207
* Bump Topic Check - used by posting and viewtopic
208
*/
209
function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
210
{
211
        global $config, $auth, $user;
212
213
        // Check permission and make sure the last post was not already bumped
214
        if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
215
        {
216
                return false;
217
        }
218
219
        // Check bump time range, is the user really allowed to bump the topic at this time?
220
        $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
221
222
        // Check bump time
223
        if ($last_post_time + $bump_time > time())
224
        {
225
                return false;
226
        }
227
228
        // Check bumper, only topic poster and last poster are allowed to bump
229
        if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'])
230
        {
231
                return false;
232
        }
233
234
        // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
235
        return $bump_time;
236
}
237
238
/**
239
* Generates a text with approx. the specified length which contains the specified words and their context
240
*
241
* @param        string        $text        The full text from which context shall be extracted
242
* @param        string        $words        An array of words which should be contained in the result, has to be a valid part of a PCRE pattern (escape with preg_quote!)
243
* @param        int                $length        The desired length of the resulting text, however the result might be shorter or longer than this value
244
*
245
* @return        string                        Context of the specified words separated by "..."
246
*/
247
function get_context($text, $words, $length = 400)
248
{
249
        // first replace all whitespaces with single spaces
250
        $text = preg_replace('/ +/', ' ', strtr($text, "\t\n\r\x0C ", '     '));
251
252
        // we need to turn the entities back into their original form, to not cut the message in between them
253
        $entities = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
254
        $characters = array('<', '>', '[', ']', '.', ':', ':');
255
        $text = str_replace($entities, $characters, $text);
256
257
        $word_indizes = array();
258
        if (sizeof($words))
259
        {
260
                $match = '';
261
                // find the starting indizes of all words
262
                foreach ($words as $word)
263
                {
264
                        if ($word)
265
                        {
266
                                if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match))
267
                                {
268
                                        if (empty($match[1]))
269
                                        {
270
                                                continue;
271
                                        }
272
273
                                        $pos = utf8_strpos($text, $match[1]);
274
                                        if ($pos !== false)
275
                                        {
276
                                                $word_indizes[] = $pos;
277
                                        }
278
                                }
279
                        }
280
                }
281
                unset($match);
282
283
                if (sizeof($word_indizes))
284
                {
285
                        $word_indizes = array_unique($word_indizes);
286
                        sort($word_indizes);
287
288
                        $wordnum = sizeof($word_indizes);
289
                        // number of characters on the right and left side of each word
290
                        $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
291
                        $final_text = '';
292
                        $word = $j = 0;
293
                        $final_text_index = -1;
294
295
                        // cycle through every character in the original text
296
                        for ($i = $word_indizes[$word], $n = utf8_strlen($text); $i < $n; $i++)
297
                        {
298
                                // if the current position is the start of one of the words then append $sequence_length characters to the final text
299
                                if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
300
                                {
301
                                        if ($final_text_index < $i - $sequence_length - 1)
302
                                        {
303
                                                $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', utf8_substr($text, $i - $sequence_length, $sequence_length));
304
                                        }
305
                                        else
306
                                        {
307
                                                // if the final text is already nearer to the current word than $sequence_length we only append the text
308
                                                // from its current index on and distribute the unused length to all other sequenes
309
                                                $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
310
                                                $final_text .= utf8_substr($text, $final_text_index + 1, $i - $final_text_index - 1);
311
                                        }
312
                                        $final_text_index = $i - 1;
313
314
                                        // add the following characters to the final text (see below)
315
                                        $word++;
316
                                        $j = 1;
317
                                }
318
319
                                if ($j > 0)
320
                                {
321
                                        // add the character to the final text and increment the sequence counter
322
                                        $final_text .= utf8_substr($text, $i, 1);
323
                                        $final_text_index++;
324
                                        $j++;
325
326
                                        // if this is a whitespace then check whether we are done with this sequence
327
                                        if (utf8_substr($text, $i, 1) == ' ')
328
                                        {
329
                                                // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
330
                                                if ($i + 4 < $n)
331
                                                {
332
                                                        if (($j > $sequence_length && $word >= $wordnum) || utf8_strlen($final_text) > $length)
333
                                                        {
334
                                                                $final_text .= ' ...';
335
                                                                break;
336
                                                        }
337
                                                }
338
                                                else
339
                                                {
340
                                                        // make sure the text really reaches the end
341
                                                        $j -= 4;
342
                                                }
343
344
                                                // stop context generation and wait for the next word
345
                                                if ($j > $sequence_length)
346
                                                {
347
                                                        $j = 0;
348
                                                }
349
                                        }
350
                                }
351
                        }
352
                        return str_replace($characters, $entities, $final_text);
353
                }
354
        }
355
356
        if (!sizeof($words) || !sizeof($word_indizes))
357
        {
358
                return str_replace($characters, $entities, ((utf8_strlen($text) >= $length + 3) ? utf8_substr($text, 0, $length) . '...' : $text));
359
        }
360
}
361
362
/**
363
* Decode text whereby text is coming from the db and expected to be pre-parsed content
364
* We are placing this outside of the message parser because we are often in need of it...
365
*/
366
function decode_message(&$message, $bbcode_uid = '')
367
{
368
        global $config;
369
370
        if ($bbcode_uid)
371
        {
372
                $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
373
                $replace = array("\n", '', '', '', '');
374
        }
375
        else
376
        {
377
                $match = array('<br />');
378
                $replace = array("\n");
379
        }
380
381
        $message = str_replace($match, $replace, $message);
382
383
        $match = get_preg_expression('bbcode_htm');
384
        $replace = array('\1', '\1', '\2', '\1', '', '');
385
386
        $message = preg_replace($match, $replace, $message);
387
}
388
389
/**
390
* Strips all bbcode from a text and returns the plain content
391
*/
392
function strip_bbcode(&$text, $uid = '')
393
{
394
        if (!$uid)
395
        {
396
                $uid = '[0-9a-z]{5,}';
397
        }
398
399
        $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=(?:&quot;.*&quot;|[^\]]*))?(?::[a-z])?(\:$uid)\]#", ' ', $text);
400
401
        $match = get_preg_expression('bbcode_htm');
402
        $replace = array('\1', '\1', '\2', '\1', '', '');
403
404
        $text = preg_replace($match, $replace, $text);
405
}
406
407
/**
408
* For display of custom parsed text on user-facing pages
409
* Expects $text to be the value directly from the database (stored value)
410
*/
411
function generate_text_for_display($text, $uid, $bitfield, $flags)
412
{
413
        static $bbcode;
414
415
        if (!$text)
416
        {
417
                return '';
418
        }
419
420
        $text = censor_text($text);
421
422
        // Parse bbcode if bbcode uid stored and bbcode enabled
423
        if ($uid && ($flags & OPTION_FLAG_BBCODE))
424
        {
425
                if (!class_exists('bbcode'))
426
                {
427
                        global $phpbb_root_path, $phpEx;
428
                        include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
429
                }
430
431
                if (empty($bbcode))
432
                {
433
                        $bbcode = new bbcode($bitfield);
434
                }
435
                else
436
                {
437
                        $bbcode->bbcode($bitfield);
438
                }
439
440
                $bbcode->bbcode_second_pass($text, $uid);
441
        }
442
443
        $text = bbcode_nl2br($text);
444
        $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES));
445
446
        return $text;
447
}
448
449
/**
450
* For parsing custom parsed text to be stored within the database.
451
* This function additionally returns the uid and bitfield that needs to be stored.
452
* Expects $text to be the value directly from request_var() and in it's non-parsed form
453
*/
454
function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false)
455
{
456
        global $phpbb_root_path, $phpEx;
457
458
        $uid = $bitfield = '';
459
        $flags = (($allow_bbcode) ? OPTION_FLAG_BBCODE : 0) + (($allow_smilies) ? OPTION_FLAG_SMILIES : 0) + (($allow_urls) ? OPTION_FLAG_LINKS : 0);
460
461
        if (!$text)
462
        {
463
                return;
464
        }
465
466
        if (!class_exists('parse_message'))
467
        {
468
                include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
469
        }
470
471
        $message_parser = new parse_message($text);
472
        $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies);
473
474
        $text = $message_parser->message;
475
        $uid = $message_parser->bbcode_uid;
476
477
        // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
478
        if (!$message_parser->bbcode_bitfield)
479
        {
480
                $uid = '';
481
        }
482
483
        $bitfield = $message_parser->bbcode_bitfield;
484
485
        return;
486
}
487
488
/**
489
* For decoding custom parsed text for edits as well as extracting the flags
490
* Expects $text to be the value directly from the database (pre-parsed content)
491
*/
492
function generate_text_for_edit($text, $uid, $flags)
493
{
494
        global $phpbb_root_path, $phpEx;
495
496
        decode_message($text, $uid);
497
498
        return array(
499
                'allow_bbcode'        => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0,
500
                'allow_smilies'        => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0,
501
                'allow_urls'        => ($flags & OPTION_FLAG_LINKS) ? 1 : 0,
502
                'text'                        => $text
503
        );
504
}
505
506
/**
507
* A subroutine of make_clickable used with preg_replace
508
* It places correct HTML around an url, shortens the displayed text
509
* and makes sure no entities are inside URLs
510
*/
511
function make_clickable_callback($type, $whitespace, $url, $relative_url, $class)
512
{
513
        $orig_url                = $url;
514
        $orig_relative        = $relative_url;
515
        $append                        = '';
516
        $url                        = htmlspecialchars_decode($url);
517
        $relative_url        = htmlspecialchars_decode($relative_url);
518
519
        // make sure no HTML entities were matched
520
        $chars = array('<', '>', '"');
521
        $split = false;
522
523
        foreach ($chars as $char)
524
        {
525
                $next_split = strpos($url, $char);
526
                if ($next_split !== false)
527
                {
528
                        $split = ($split !== false) ? min($split, $next_split) : $next_split;
529
                }
530
        }
531
532
        if ($split !== false)
533
        {
534
                // an HTML entity was found, so the URL has to end before it
535
                $append                        = substr($url, $split) . $relative_url;
536
                $url                        = substr($url, 0, $split);
537
                $relative_url        = '';
538
        }
539
        else if ($relative_url)
540
        {
541
                // same for $relative_url
542
                $split = false;
543
                foreach ($chars as $char)
544
                {
545
                        $next_split = strpos($relative_url, $char);
546
                        if ($next_split !== false)
547
                        {
548
                                $split = ($split !== false) ? min($split, $next_split) : $next_split;
549
                        }
550
                }
551
552
                if ($split !== false)
553
                {
554
                        $append                        = substr($relative_url, $split);
555
                        $relative_url        = substr($relative_url, 0, $split);
556
                }
557
        }
558
559
        // if the last character of the url is a punctuation mark, exclude it from the url
560
        $last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1];
561
562
        switch ($last_char)
563
        {
564
                case '.':
565
                case '?':
566
                case '!':
567
                case ':':
568
                case ',':
569
                        $append = $last_char;
570
                        if ($relative_url)
571
                        {
572
                                $relative_url = substr($relative_url, 0, -1);
573
                        }
574
                        else
575
                        {
576
                                $url = substr($url, 0, -1);
577
                        }
578
                break;
579
580
                // set last_char to empty here, so the variable can be used later to
581
                // check whether a character was removed
582
                default:
583
                        $last_char = '';
584
                break;
585
        }
586
587
        $short_url = (strlen($url) > 55) ? substr($url, 0, 39) . ' ... ' . substr($url, -10) : $url;
588
589
        switch ($type)
590
        {
591
                case MAGIC_URL_LOCAL:
592
                        $tag                        = 'l';
593
                        $relative_url        = preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url));
594
                        $url                        = $url . '/' . $relative_url;
595
                        $text                        = $relative_url;
596
597
                        // this url goes to http://domain.tld/path/to/board/ which
598
                        // would result in an empty link if treated as local so
599
                        // don't touch it and let MAGIC_URL_FULL take care of it.
600
                        if (!$relative_url)
601
                        {
602
                                return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern
603
                        }
604
                break;
605
606
                case MAGIC_URL_FULL:
607
                        $tag        = 'm';
608
                        $text        = $short_url;
609
                break;
610
611
                case MAGIC_URL_WWW:
612
                        $tag        = 'w';
613
                        $url        = 'http://' . $url;
614
                        $text        = $short_url;
615
                break;
616
617
                case MAGIC_URL_EMAIL:
618
                        $tag        = 'e';
619
                        $text        = $short_url;
620
                        $url        = 'mailto:' . $url;
621
                break;
622
        }
623
624
        $url        = htmlspecialchars($url);
625
        $text        = htmlspecialchars($text);
626
        $append        = htmlspecialchars($append);
627
628
        $html        = "$whitespace<!-- $tag --><a$class href=\"$url\">$text</a><!-- $tag -->$append";
629
630
        return $html;
631
}
632
633
/**
634
* make_clickable function
635
*
636
* Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
637
* Cuts down displayed size of link if over 50 chars, turns absolute links
638
* into relative versions when the server/script path matches the link
639
*/
640
function make_clickable($text, $server_url = false, $class = 'postlink')
641
{
642
        if ($server_url === false)
643
        {
644
                $server_url = generate_board_url();
645
        }
646
647
        static $magic_url_match;
648
        static $magic_url_replace;
649
        static $static_class;
650
651
        if (!is_array($magic_url_match) || $static_class != $class)
652
        {
653
                $static_class = $class;
654
                $class = ($static_class) ? ' class="' . $static_class . '"' : '';
655
                $local_class = ($static_class) ? ' class="' . $static_class . '-local"' : '';
656
657
                $magic_url_match = $magic_url_replace = array();
658
                // Be sure to not let the matches cross over. ;)
659
660
                // relative urls for this board
661
                $magic_url_match[] = '#(^|[\n\t (>.])(' . preg_quote($server_url, '#') . ')/(' . get_preg_expression('relative_url_inline') . ')#ie';
662
                $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_LOCAL, '\$1', '\$2', '\$3', '$local_class')";
663
664
                // matches a xxxx://aaaaa.bbb.cccc. ...
665
                $magic_url_match[] = '#(^|[\n\t (>.])(' . get_preg_expression('url_inline') . ')#ie';
666
                $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_FULL, '\$1', '\$2', '', '$class')";
667
668
                // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
669
                $magic_url_match[] = '#(^|[\n\t (>])(' . get_preg_expression('www_url_inline') . ')#ie';
670
                $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_WWW, '\$1', '\$2', '', '$class')";
671
672
                // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
673
                $magic_url_match[] = '/(^|[\n\t (>])(' . get_preg_expression('email') . ')/ie';
674
                $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_EMAIL, '\$1', '\$2', '', '')";
675
        }
676
677
        return preg_replace($magic_url_match, $magic_url_replace, $text);
678
}
679
680
/**
681
* Censoring
682
*/
683
function censor_text($text)
684
{
685
        static $censors;
686
687
        // Nothing to do?
688
        if ($text === '')
689
        {
690
                return '';
691
        }
692
693
        // We moved the word censor checks in here because we call this function quite often - and then only need to do the check once
694
        if (!isset($censors) || !is_array($censors))
695
        {
696
                global $config, $user, $auth, $cache;
697
698
                // We check here if the user is having viewing censors disabled (and also allowed to do so).
699
                if (!$user->optionget('viewcensors') && $config['allow_nocensors'] && $auth->acl_get('u_chgcensors'))
700
                {
701
                        $censors = array();
702
                }
703
                else
704
                {
705
                        $censors = $cache->obtain_word_list();
706
                }
707
        }
708
709
        if (sizeof($censors))
710
        {
711
                return preg_replace($censors['match'], $censors['replace'], $text);
712
        }
713
714
        return $text;
715
}
716
717
/**
718
* custom version of nl2br which takes custom BBCodes into account
719
*/
720
function bbcode_nl2br($text)
721
{
722
        // custom BBCodes might contain carriage returns so they
723
        // are not converted into <br /> so now revert that
724
        $text = str_replace(array("\n", "\r"), array('<br />', "\n"), $text);
725
        return $text;
726
}
727
728
/**
729
* Smiley processing
730
*/
731
function smiley_text($text, $force_option = false)
732
{
733
        global $config, $user, $phpbb_root_path;
734
735
        if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
736
        {
737
                return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
738
        }
739
        else
740
        {
741
                $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path;
742
                return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img src="' . $root_path . $config['smilies_path'] . '/\2 />', $text);
743
        }
744
}
745
746
/**
747
* General attachment parsing
748
*
749
* @param mixed $forum_id The forum id the attachments are displayed in (false if in private message)
750
* @param string &$message The post/private message
751
* @param array &$attachments The attachments to parse for (inline) display. The attachments array will hold templated data after parsing.
752
* @param array &$update_count The attachment counts to be updated - will be filled
753
* @param bool $preview If set to true the attachments are parsed for preview. Within preview mode the comments are fetched from the given $attachments array and not fetched from the database.
754
*/
755
function parse_attachments($forum_id, &$message, &$attachments, &$update_count, $preview = false)
756
{
757
        if (!sizeof($attachments))
758
        {
759
                return;
760
        }
761
762
        global $template, $cache, $user;
763
        global $extensions, $config, $phpbb_root_path, $phpEx;
764
765
        //
766
        $compiled_attachments = array();
767
768
        if (!isset($template->filename['attachment_tpl']))
769
        {
770
                $template->set_filenames(array(
771
                        'attachment_tpl'        => 'attachment.html')
772
                );
773
        }
774
775
        if (empty($extensions) || !is_array($extensions))
776
        {
777
                $extensions = $cache->obtain_attach_extensions($forum_id);
778
        }
779
780
        // Look for missing attachment information...
781
        $attach_ids = array();
782
        foreach ($attachments as $pos => $attachment)
783
        {
784
                // If is_orphan is set, we need to retrieve the attachments again...
785
                if (!isset($attachment['extension']) && !isset($attachment['physical_filename']))
786
                {
787
                        $attach_ids[(int) $attachment['attach_id']] = $pos;
788
                }
789
        }
790
791
        // Grab attachments (security precaution)
792
        if (sizeof($attach_ids))
793
        {
794
                global $db;
795
796
                $new_attachment_data = array();
797
798
                $sql = 'SELECT *
799
                        FROM ' . ATTACHMENTS_TABLE . '
800
                        WHERE ' . $db->sql_in_set('attach_id', array_keys($attach_ids));
801
                $result = $db->sql_query($sql);
802
803
                while ($row = $db->sql_fetchrow($result))
804
                {
805
                        if (!isset($attach_ids[$row['attach_id']]))
806
                        {
807
                                continue;
808
                        }
809
810
                        // If we preview attachments we will set some retrieved values here
811
                        if ($preview)
812
                        {
813
                                $row['attach_comment'] = $attachments[$attach_ids[$row['attach_id']]]['attach_comment'];
814
                        }
815
816
                        $new_attachment_data[$attach_ids[$row['attach_id']]] = $row;
817
                }
818
                $db->sql_freeresult($result);
819
820
                $attachments = $new_attachment_data;
821
                unset($new_attachment_data);
822
        }
823
824
        // Sort correctly
825
        if ($config['display_order'])
826
        {
827
                // Ascending sort
828
                krsort($attachments);
829
        }
830
        else
831
        {
832
                // Descending sort
833
                ksort($attachments);
834
        }
835
836
        foreach ($attachments as $attachment)
837
        {
838
                if (!sizeof($attachment))
839
                {
840
                        continue;
841
                }
842
843
                // We need to reset/empty the _file block var, because this function might be called more than once
844
                $template->destroy_block_vars('_file');
845
846
                $block_array = array();
847
848
                // Some basics...
849
                $attachment['extension'] = strtolower(trim($attachment['extension']));
850
                $filename = $phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($attachment['physical_filename']);
851
                $thumbnail_filename = $phpbb_root_path . $config['upload_path'] . '/thumb_' . utf8_basename($attachment['physical_filename']);
852
853
                $upload_icon = '';
854
855
                if (isset($extensions[$attachment['extension']]))
856
                {
857
                        if ($user->img('icon_topic_attach', '') && !$extensions[$attachment['extension']]['upload_icon'])
858
                        {
859
                                $upload_icon = $user->img('icon_topic_attach', '');
860
                        }
861
                        else if ($extensions[$attachment['extension']]['upload_icon'])
862
                        {
863
                                $upload_icon = '<img src="' . $phpbb_root_path . $config['upload_icons_path'] . '/' . trim($extensions[$attachment['extension']]['upload_icon']) . '" alt="" />';
864
                        }
865
                }
866
867
                $filesize = get_formatted_filesize($attachment['filesize'], false);
868
869
                $comment = bbcode_nl2br(censor_text($attachment['attach_comment']));
870
871
                $block_array += array(
872
                        'UPLOAD_ICON'                => $upload_icon,
873
                        'FILESIZE'                        => $filesize['value'],
874
                        'SIZE_LANG'                        => $filesize['unit'],
875
                        'DOWNLOAD_NAME'                => utf8_basename($attachment['real_filename']),
876
                        'COMMENT'                        => $comment,
877
                );
878
879
                $denied = false;
880
881
                if (!extension_allowed($forum_id, $attachment['extension'], $extensions))
882
                {
883
                        $denied = true;
884
885
                        $block_array += array(
886
                                'S_DENIED'                        => true,
887
                                'DENIED_MESSAGE'        => sprintf($user->lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension'])
888
                        );
889
                }
890
891
                if (!$denied)
892
                {
893
                        $l_downloaded_viewed = $download_link = '';
894
                        $display_cat = $extensions[$attachment['extension']]['display_cat'];
895
896
                        if ($display_cat == ATTACHMENT_CATEGORY_IMAGE)
897
                        {
898
                                if ($attachment['thumbnail'])
899
                                {
900
                                        $display_cat = ATTACHMENT_CATEGORY_THUMB;
901
                                }
902
                                else
903
                                {
904
                                        if ($config['img_display_inlined'])
905
                                        {
906
                                                if ($config['img_link_width'] || $config['img_link_height'])
907
                                                {
908
                                                        $dimension = @getimagesize($filename);
909
910
                                                        // If the dimensions could not be determined or the image being 0x0 we display it as a link for safety purposes
911
                                                        if ($dimension === false || empty($dimension[0]) || empty($dimension[1]))
912
                                                        {
913
                                                                $display_cat = ATTACHMENT_CATEGORY_NONE;
914
                                                        }
915
                                                        else
916
                                                        {
917
                                                                $display_cat = ($dimension[0] <= $config['img_link_width'] && $dimension[1] <= $config['img_link_height']) ? ATTACHMENT_CATEGORY_IMAGE : ATTACHMENT_CATEGORY_NONE;
918
                                                        }
919
                                                }
920
                                        }
921
                                        else
922
                                        {
923
                                                $display_cat = ATTACHMENT_CATEGORY_NONE;
924
                                        }
925
                                }
926
                        }
927
928
                        // Make some descisions based on user options being set.
929
                        if (($display_cat == ATTACHMENT_CATEGORY_IMAGE || $display_cat == ATTACHMENT_CATEGORY_THUMB) && !$user->optionget('viewimg'))
930
                        {
931
                                $display_cat = ATTACHMENT_CATEGORY_NONE;
932
                        }
933
934
                        if ($display_cat == ATTACHMENT_CATEGORY_FLASH && !$user->optionget('viewflash'))
935
                        {
936
                                $display_cat = ATTACHMENT_CATEGORY_NONE;
937
                        }
938
939
                        $download_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
940
                        $l_downloaded_viewed = 'VIEWED_COUNTS';
941
942
                        switch ($display_cat)
943
                        {
944
                                // Images
945
                                case ATTACHMENT_CATEGORY_IMAGE:
946
                                        $inline_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
947
                                        $download_link .= '&amp;mode=view';
948
949
                                        $block_array += array(
950
                                                'S_IMAGE'                => true,
951
                                                'U_INLINE_LINK'                => $inline_link,
952
                                        );
953
954
                                        $update_count[] = $attachment['attach_id'];
955
                                break;
956
957
                                // Images, but display Thumbnail
958
                                case ATTACHMENT_CATEGORY_THUMB:
959
                                        $thumbnail_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id'] . '&amp;t=1');
960
                                        $download_link .= '&amp;mode=view';
961
962
                                        $block_array += array(
963
                                                'S_THUMBNAIL'                => true,
964
                                                'THUMB_IMAGE'                => $thumbnail_link,
965
                                        );
966
967
                                        $update_count[] = $attachment['attach_id'];
968
                                break;
969
970
                                // Windows Media Streams
971
                                case ATTACHMENT_CATEGORY_WM:
972
973
                                        // Giving the filename directly because within the wm object all variables are in local context making it impossible
974
                                        // to validate against a valid session (all params can differ)
975
                                        // $download_link = $filename;
976
977
                                        $block_array += array(
978
                                                'U_FORUM'                => generate_board_url(),
979
                                                'ATTACH_ID'                => $attachment['attach_id'],
980
                                                'S_WM_FILE'                => true,
981
                                        );
982
983
                                        // Viewed/Heared File ... update the download count
984
                                        $update_count[] = $attachment['attach_id'];
985
                                break;
986
987
                                // Real Media Streams
988
                                case ATTACHMENT_CATEGORY_RM:
989
                                case ATTACHMENT_CATEGORY_QUICKTIME:
990
991
                                        $block_array += array(
992
                                                'S_RM_FILE'                        => ($display_cat == ATTACHMENT_CATEGORY_RM) ? true : false,
993
                                                'S_QUICKTIME_FILE'        => ($display_cat == ATTACHMENT_CATEGORY_QUICKTIME) ? true : false,
994
                                                'U_FORUM'                        => generate_board_url(),
995
                                                'ATTACH_ID'                        => $attachment['attach_id'],
996
                                        );
997
998
                                        // Viewed/Heared File ... update the download count
999
                                        $update_count[] = $attachment['attach_id'];
1000
                                break;
1001
1002
                                // Macromedia Flash Files
1003
                                case ATTACHMENT_CATEGORY_FLASH:
1004
                                        list($width, $height) = @getimagesize($filename);
1005
1006
                                        $block_array += array(
1007
                                                'S_FLASH_FILE'        => true,
1008
                                                'WIDTH'                        => $width,
1009
                                                'HEIGHT'                => $height,
1010
                                                'U_VIEW_LINK'        => $download_link . '&amp;view=1',
1011
                                        );
1012
1013
                                        // Viewed/Heared File ... update the download count
1014
                                        $update_count[] = $attachment['attach_id'];
1015
                                break;
1016
1017
                                default:
1018
                                        $l_downloaded_viewed = 'DOWNLOAD_COUNTS';
1019
1020
                                        $block_array += array(
1021
                                                'S_FILE'                => true,
1022
                                        );
1023
                                break;
1024
                        }
1025
1026
                        if (!isset($attachment['download_count']))
1027
                        {
1028
                                $attachment['download_count'] = 0;
1029
                        }
1030
1031
                        $block_array += array(
1032
                                'U_DOWNLOAD_LINK'                => $download_link,
1033
                                'L_DOWNLOAD_COUNT'                => $user->lang($l_downloaded_viewed, (int) $attachment['download_count']),
1034
                        );
1035
                }
1036
1037
                $template->assign_block_vars('_file', $block_array);
1038
1039
                $compiled_attachments[] = $template->assign_display('attachment_tpl');
1040
        }
1041
1042
        $attachments = $compiled_attachments;
1043
        unset($compiled_attachments);
1044
1045
        $tpl_size = sizeof($attachments);
1046
1047
        $unset_tpl = array();
1048
1049
        preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $message, $matches, PREG_PATTERN_ORDER);
1050
1051
        $replace = array();
1052
        foreach ($matches[0] as $num => $capture)
1053
        {
1054
                // Flip index if we are displaying the reverse way
1055
                $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num];
1056
1057
                $replace['from'][] = $matches[0][$num];
1058
                $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
1059
1060
                $unset_tpl[] = $index;
1061
        }
1062
1063
        if (isset($replace['from']))
1064
        {
1065
                $message = str_replace($replace['from'], $replace['to'], $message);
1066
        }
1067
1068
        $unset_tpl = array_unique($unset_tpl);
1069
1070
        // Needed to let not display the inlined attachments at the end of the post again
1071
        foreach ($unset_tpl as $index)
1072
        {
1073
                unset($attachments[$index]);
1074
        }
1075
}
1076
1077
/**
1078
* Check if extension is allowed to be posted.
1079
*
1080
* @param mixed $forum_id The forum id to check or false if private message
1081
* @param string $extension The extension to check, for example zip.
1082
* @param array &$extensions The extension array holding the information from the cache (will be obtained if empty)
1083
*
1084
* @return bool False if the extension is not allowed to be posted, else true.
1085
*/
1086
function extension_allowed($forum_id, $extension, &$extensions)
1087
{
1088
        if (empty($extensions))
1089
        {
1090
                global $cache;
1091
                $extensions = $cache->obtain_attach_extensions($forum_id);
1092
        }
1093
1094
        return (!isset($extensions['_allowed_'][$extension])) ? false : true;
1095
}
1096
1097
/**
1098
* Truncates string while retaining special characters if going over the max length
1099
* The default max length is 60 at the moment
1100
* The maximum storage length is there to fit the string within the given length. The string may be further truncated due to html entities.
1101
* For example: string given is 'a "quote"' (length: 9), would be a stored as 'a &quot;quote&quot;' (length: 19)
1102
*
1103
* @param string $string The text to truncate to the given length. String is specialchared.
1104
* @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char)
1105
* @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars).
1106
* @param bool $allow_reply Allow Re: in front of string 
1107
*         NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated. 
1108
* @param string $append String to be appended
1109
*/
1110
function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '')
1111
{
1112
        $chars = array();
1113
1114
        $strip_reply = false;
1115
        $stripped = false;
1116
        if ($allow_reply && strpos($string, 'Re: ') === 0)
1117
        {
1118
                $strip_reply = true;
1119
                $string = substr($string, 4);
1120
        }
1121
1122
        $_chars = utf8_str_split(htmlspecialchars_decode($string));
1123
        $chars = array_map('utf8_htmlspecialchars', $_chars);
1124
1125
        // Now check the length ;)
1126
        if (sizeof($chars) > $max_length)
1127
        {
1128
                // Cut off the last elements from the array
1129
                $string = implode('', array_slice($chars, 0, $max_length - utf8_strlen($append)));
1130
                $stripped = true;
1131
        }
1132
1133
        // Due to specialchars, we may not be able to store the string...
1134
        if (utf8_strlen($string) > $max_store_length)
1135
        {
1136
                // let's split again, we do not want half-baked strings where entities are split
1137
                $_chars = utf8_str_split(htmlspecialchars_decode($string));
1138
                $chars = array_map('utf8_htmlspecialchars', $_chars);
1139
1140
                do
1141
                {
1142
                        array_pop($chars);
1143
                        $string = implode('', $chars);
1144
                }
1145
                while (!empty($chars) && utf8_strlen($string) > $max_store_length);
1146
        }
1147
1148
        if ($strip_reply)
1149
        {
1150
                $string = 'Re: ' . $string;
1151
        }
1152
1153
        if ($append != '' && $stripped)
1154
        {
1155
                $string = $string . $append;
1156
        }
1157
1158
        return $string;
1159
}
1160
1161
/**
1162
* Get username details for placing into templates.
1163
* This function caches all modes on first call, except for no_profile and anonymous user - determined by $user_id.
1164
*
1165
* @param string $mode Can be profile (for getting an url to the profile), username (for obtaining the username), colour (for obtaining the user colour), full (for obtaining a html string representing a coloured link to the users profile) or no_profile (the same as full but forcing no profile link)
1166
* @param int $user_id The users id
1167
* @param string $username The users name
1168
* @param string $username_colour The users colour
1169
* @param string $guest_username optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then.
1170
* @param string $custom_profile_url optional parameter to specify a profile url. The user id get appended to this url as &amp;u={user_id}
1171
*
1172
* @return string A string consisting of what is wanted based on $mode.
1173
* @author BartVB, Acyd Burn
1174
*/
1175
function get_username_string($mode, $user_id, $username, $username_colour = '', $guest_username = false, $custom_profile_url = false)
1176
{
1177
        static $_profile_cache;
1178
1179
        // We cache some common variables we need within this function
1180
        if (empty($_profile_cache))
1181
        {
1182
                global $phpbb_root_path, $phpEx;
1183
1184
                $_profile_cache['base_url'] = append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&amp;u={USER_ID}');
1185
                $_profile_cache['tpl_noprofile'] = '{USERNAME}';
1186
                $_profile_cache['tpl_noprofile_colour'] = '<span style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</span>';
1187
                $_profile_cache['tpl_profile'] = '<a href="{PROFILE_URL}">{USERNAME}</a>';
1188
                $_profile_cache['tpl_profile_colour'] = '<a href="{PROFILE_URL}" style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</a>';
1189
        }
1190
1191
        global $user, $auth;
1192
1193
        // This switch makes sure we only run code required for the mode
1194
        switch ($mode)
1195
        {
1196
                case 'full':
1197
                case 'no_profile':
1198
                case 'colour':
1199
1200
                        // Build correct username colour
1201
                        $username_colour = ($username_colour) ? '#' . $username_colour : '';
1202
1203
                        // Return colour
1204
                        if ($mode == 'colour')
1205
                        {
1206
                                return $username_colour;
1207
                        }
1208
1209
                // no break;
1210
1211
                case 'username':
1212
1213
                        // Build correct username
1214
                        if ($guest_username === false)
1215
                        {
1216
                                $username = ($username) ? $username : $user->lang['GUEST'];
1217
                        }
1218
                        else
1219
                        {
1220
                                $username = ($user_id && $user_id != ANONYMOUS) ? $username : ((!empty($guest_username)) ? $guest_username : $user->lang['GUEST']);
1221
                        }
1222
1223
                        // Return username
1224
                        if ($mode == 'username')
1225
                        {
1226
                                return $username;
1227
                        }
1228
1229
                // no break;
1230
1231
                case 'profile':
1232
1233
                        // Build correct profile url - only show if not anonymous and permission to view profile if registered user
1234
                        // For anonymous the link leads to a login page.
1235
                        if ($user_id && $user_id != ANONYMOUS && ($user->data['user_id'] == ANONYMOUS || $auth->acl_get('u_viewprofile')))
1236
                        {
1237
                                $profile_url = ($custom_profile_url !== false) ? $custom_profile_url . '&amp;u=' . (int) $user_id : str_replace(array('={USER_ID}', '=%7BUSER_ID%7D'), '=' . (int) $user_id, $_profile_cache['base_url']);
1238
                        }
1239
                        else
1240
                        {
1241
                                $profile_url = '';
1242
                        }
1243
1244
                        // Return profile
1245
                        if ($mode == 'profile')
1246
                        {
1247
                                return $profile_url;
1248
                        }
1249
1250
                // no break;
1251
        }
1252
1253
        if (($mode == 'full' && !$profile_url) || $mode == 'no_profile')
1254
        {
1255
                return str_replace(array('{USERNAME_COLOUR}', '{USERNAME}'), array($username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_noprofile'] : $_profile_cache['tpl_noprofile_colour']);
1256
        }
1257
1258
        return str_replace(array('{PROFILE_URL}', '{USERNAME_COLOUR}', '{USERNAME}'), array($profile_url, $username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_profile'] : $_profile_cache['tpl_profile_colour']);
1259
}
1260
1261
/**
1262
* @package phpBB3
1263
*/
1264
class bitfield
1265
{
1266
        var $data;
1267
1268
        function bitfield($bitfield = '')
1269
        {
1270
                $this->data = base64_decode($bitfield);
1271
        }
1272
1273
        /**
1274
        */
1275
        function get($n)
1276
        {
1277
                // Get the ($n / 8)th char
1278
                $byte = $n >> 3;
1279
1280
                if (strlen($this->data) >= $byte + 1)
1281
                {
1282
                        $c = $this->data[$byte];
1283
1284
                        // Lookup the ($n % 8)th bit of the byte
1285
                        $bit = 7 - ($n & 7);
1286
                        return (bool) (ord($c) & (1 << $bit));
1287
                }
1288
                else
1289
                {
1290
                        return false;
1291
                }
1292
        }
1293
1294
        function set($n)
1295
        {
1296
                $byte = $n >> 3;
1297
                $bit = 7 - ($n & 7);
1298
1299
                if (strlen($this->data) >= $byte + 1)
1300
                {
1301
                        $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
1302
                }
1303
                else
1304
                {
1305
                        $this->data .= str_repeat("\0", $byte - strlen($this->data));
1306
                        $this->data .= chr(1 << $bit);
1307
                }
1308
        }
1309
1310
        function clear($n)
1311
        {
1312
                $byte = $n >> 3;
1313
1314
                if (strlen($this->data) >= $byte + 1)
1315
                {
1316
                        $bit = 7 - ($n & 7);
1317
                        $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
1318
                }
1319
        }
1320
1321
        function get_blob()
1322
        {
1323
                return $this->data;
1324
        }
1325
1326
        function get_base64()
1327
        {
1328
                return base64_encode($this->data);
1329
        }
1330
1331
        function get_bin()
1332
        {
1333
                $bin = '';
1334
                $len = strlen($this->data);
1335
1336
                for ($i = 0; $i < $len; ++$i)
1337
                {
1338
                        $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
1339
                }
1340
1341
                return $bin;
1342
        }
1343
1344
        function get_all_set()
1345
        {
1346
                return array_keys(array_filter(str_split($this->get_bin())));
1347
        }
1348
1349
        function merge($bitfield)
1350
        {
1351
                $this->data = $this->data | $bitfield->get_blob();
1352
        }
1353
}