• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cypht-org / cypht / 18215089059

03 Oct 2025 06:45AM UTC coverage: 79.651% (-0.04%) from 79.688%
18215089059

push

travis-ci

web-flow
Merge pull request #1690 from Grandi0z/cypht-enh-contact-menu

refactor(frontend): improve contact list display with tooltips and responsive styles

4795 of 6020 relevant lines covered (79.65%)

17.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.34
/modules/core/message_functions.php
1
<?php
2

3
/**
4
 * Core modules
5
 * @package modules
6
 * @subpackage core
7
 */
8

9
/**
10
 * Format a message body that has HMTL markup
11
 * @subpackage core/functions
12
 * @param string $str message HTML
13
 * @param bool $images allow external images
14
 * @return string
15
 */
16
if (!hm_exists('format_msg_html')) {
187✔
17
function format_msg_html($str, $images=false) {
18
    $str = mb_eregi_replace('</body>', '', $str);
2✔
19

20
    $config = HTMLPurifier_Config::createDefault();
2✔
21
    $config->set('HTML.DefinitionID', 'hm-message');
2✔
22
    $config->set('HTML.DefinitionRev', 1);
2✔
23
    $config->set('Cache.DefinitionImpl', null);
2✔
24
    $config->set('HTML.TargetBlank', true);
2✔
25
    $config->set('HTML.TargetNoopener', true);
2✔
26

27
    if (!$images) {
2✔
28
        $config->set('URI.DisableExternalResources', true);
2✔
29
    }
30
    $config->set('URI.AllowedSchemes', array('mailto' => true, 'data' => true, 'http' => true, 'https' => true));
2✔
31
    $config->set('Filter.ExtractStyleBlocks.TidyImpl', true);
2✔
32

33
    if ($def = $config->maybeGetRawHTMLDefinition()) {
2✔
34
        $html_tags = ['img', 'script', 'iframe', 'audio', 'embed', 'source', 'track', 'video'];
2✔
35
        foreach ($html_tags as $tag) {
2✔
36
            $def->addAttribute($tag, 'data-src', 'Text');
2✔
37
        }
38
    }
39

40
    try {
41
        $purifier = new HTMLPurifier($config);
2✔
42
        return $purifier->purify($str);
2✔
43
    } catch (Exception $e) {
×
44
        return '';
×
45
    }
46
}}
47

48
/**
49
 * Sanitize HTML for email
50
 * @subpackage core/functions
51
 * @param string $html content to sanitize
52
 * @return string
53
 */
54
if (!hm_exists('sanitize_email_html')) {
187✔
55
function sanitize_email_html($html) {
56
    $html = preg_replace_callback(
×
57
        '/<([^>]+)\s*style\s*=\s*(["\'])(.*?)\2/i',
×
58
        function($matches) {
×
59
            $content = preg_replace('/background-image\s*:\s*url\([^)]*\)\s*;?\s*/i', '', $matches[3]);
×
60
            return '<' . $matches[1] . ' style=' . $matches[2] . $content . $matches[2];
×
61
        },
×
62
        $html
×
63
    );
×
64

65
    return $html;
×
66
}}
67

68
/**
69
 * Convert HTML to plain text
70
 * @param string $html content to convert
71
 * @return string
72
 */
73
if (!hm_exists('convert_html_to_text')) {
187✔
74
function convert_html_to_text($html) {
75
    $html = new HTMLToText($html);
2✔
76
    return $html->text;
2✔
77
}}
78

79
/**
80
 * Format image data
81
 * @subpackage core/functions
82
 * @param string $str binary image data
83
 * @param string $mime_type type of image
84
 * return string
85
 */
86
if (!hm_exists('format_msg_image')) {
187✔
87
function format_msg_image($str, $mime_type) {
88
    return '<img class="msg_img" alt="" src="data:image/'.$mime_type.';base64,'.chunk_split(base64_encode($str)).'" />';
1✔
89
}}
90

91
/**
92
 * Format a plain text message
93
 * @subpackage core/functions
94
 * @param string $str message text
95
 * @param object $output_mod Hm_Output_Module
96
 */
97
if (!hm_exists('format_msg_text')) {
187✔
98
function format_msg_text($str, $output_mod, $links=true) {
99
    $str = str_replace("\t", '    ', $str);
1✔
100
    $str = nl2br(str_replace(' ', '<wbr>', ($output_mod->html_safe($str)))).'<br />';
1✔
101
    $str = preg_replace("/(&(?!amp)[^;]+;)/", " $1", $str);
1✔
102
    if ($links) {
1✔
103
        $link_regex = "/((http|ftp|rtsp)s?:\/\/(%[[:digit:]A-Fa-f][[:digit:]A-Fa-f]|[-_\.!~\*';\/\?#:@&=\+$,%[:alnum:]])+)/m";
1✔
104
        $str = preg_replace($link_regex, "<a href=\"$1\" target=\"_blank\" rel=\"noopener\">$1</a>", $str);
1✔
105
    }
106
    $str = preg_replace("/ (&[^;]+;)/", "$1", $str);
1✔
107
    $str = str_replace('<wbr>', '&#160;<wbr>', $str);
1✔
108
    return preg_replace("/^(&gt;.*<br \/>)/m", "<span class=\"reply_quote\">$1</span>", $str);
1✔
109
}}
110

111
/**
112
 * Format reply text
113
 * @subpackage core/functions
114
 * @param string $txt message text
115
 * @return string
116
 */
117
if (!hm_exists('format_reply_text')) {
187✔
118
function format_reply_text($txt) {
119
    $lines = explode("\n", $txt);
5✔
120
    $new_lines = array();
5✔
121
    foreach ($lines as $line) {
5✔
122
        $pre = '> ';
5✔
123
        if (preg_match("/^(>\s*)+/", $line, $matches)) {
5✔
124
            $pre .= $matches[1];
1✔
125
        }
126
        $wrap = 75 + mb_strlen($pre);
5✔
127
        $new_lines[] = preg_replace("/$pre /", "$pre", "> ".wordwrap($line, $wrap, "\n$pre"));
5✔
128
    }
129
    return implode("\n", $new_lines);
5✔
130
}}
131

132
/**
133
 * Get reply to address
134
 * @subpackage core/functions
135
 * @param array $headers message headers
136
 * @param string $type type (forward, reply, reply_all)
137
 * @return string
138
 */
139
if (!hm_exists('reply_to_address')) {
187✔
140
function reply_to_address($headers, $type) {
141
    $msg_to = '';
2✔
142
    $msg_cc = '';
2✔
143
    $headers = lc_headers($headers);
2✔
144
    $parsed = array();
2✔
145

146
    if ($type == 'forward') {
2✔
147
        return $msg_to;
1✔
148
    }
149
    foreach (array('reply-to', 'from', 'sender', 'return-path') as $fld) {
2✔
150
        if (array_key_exists($fld, $headers)) {
2✔
151
            list($parsed, $msg_to) = format_reply_address($headers[$fld], $parsed);
2✔
152
            if ($msg_to) {
2✔
153
                break;
2✔
154
            }
155
        }
156
    }
157
    if ($type == 'reply_all') {
2✔
158
        if (array_key_exists('cc', $headers)) {
1✔
159
            list($cc_parsed, $msg_cc) = format_reply_address($headers['cc'], $parsed);
1✔
160
            $parsed += $cc_parsed;
1✔
161
        }
162
        if (array_key_exists('to', $headers)) {
1✔
163
            list($parsed, $recips) = format_reply_address($headers['to'], $parsed);
1✔
164
            if ($recips) {
1✔
165
                if ($msg_cc) {
1✔
166
                    $msg_cc .= ', '.$recips;
1✔
167
                }
168
                else {
169
                    $msg_cc = $recips;
1✔
170
                }
171
            }
172
        }
173
    }
174
    return array($msg_to, $msg_cc);
2✔
175
}}
176

177
/*
178
 * Format a reply address line
179
 * @param string $fld the field values from the E-mail being replied to
180
 * @param array $excluded list of parsed addresses to exclude
181
 * @return string
182
 */
183
if (!hm_exists('format_reply_address')) {
187✔
184
function format_reply_address($fld, $excluded) {
185
    $addr = process_address_fld(trim($fld));
2✔
186
    $res = array();
2✔
187
    foreach ($addr as $v) {
2✔
188
        $skip = false;
2✔
189
        foreach ($excluded as $ex) {
2✔
190
            if (mb_strtolower($v['email']) == mb_strtolower($ex['email'])) {
1✔
191
                $skip = true;
1✔
192
                break;
1✔
193
            }
194
        }
195
        if (!$skip) {
2✔
196
            $res[] = $v;
2✔
197
        }
198
    }
199
    if ($res) {
2✔
200
        return array($addr, implode(', ', array_map(function($v) {
2✔
201
            if (trim($v['label'])) {
2✔
202
                return str_replace([',', ';'], '', $v['label']).' '.$v['email'];
1✔
203
            }
204
            else {
205
                return $v['email'];
2✔
206
            }
207
        }, $res)));
2✔
208
    }
209
    return array($addr, '');
1✔
210
}}
211

212
/**
213
 * Get reply to subject
214
 * @subpackage core/functions
215
 * @param array $headers message headers
216
 * @param string $type type (forward, reply, reply_all)
217
 * @return string
218
 */
219
if (!hm_exists('reply_to_subject')) {
187✔
220
function reply_to_subject($headers, $type) {
221
    $subject = '';
2✔
222
    if (array_key_exists('Subject', $headers)) {
2✔
223
        if ($type == 'reply' || $type == 'reply_all') {
1✔
224
            if (!preg_match("/^re:/i", trim($headers['Subject']))) {
1✔
225
                $subject = sprintf("Re: %s", $headers['Subject']);
1✔
226
            }
227
        }
228
        elseif ($type == 'forward') {
1✔
229
            if (!preg_match("/^fwd:/i", trim($headers['Subject']))) {
1✔
230
                $subject = sprintf("Fwd: %s", $headers['Subject']);
1✔
231
            }
232
        }
233
        if (!$subject) {
1✔
234
            $subject = $headers['Subject'];
1✔
235
        }
236
    }
237
    return $subject;
2✔
238
}}
239

240
/**
241
 * Get reply message lead in
242
 * @subpackage core/functions
243
 * @param array $headers message headers
244
 * @param string $type type (forward, reply, reply_all)
245
 * @param string $to reply to value
246
 * @param object $output_mod output module object
247
 * @return string
248
 */
249
if (!hm_exists('reply_lead_in')) {
187✔
250
function reply_lead_in($headers, $type, $to, $output_mod) {
251
    $lead_in = '';
2✔
252
    if ($type == 'reply' || $type == 'reply_all') {
2✔
253
        if (array_key_exists('Date', $headers)) {
2✔
254
            if ($to) {
1✔
255
                $lead_in = sprintf($output_mod->trans('On %s %s said')."\n\n", $headers['Date'], $to);
1✔
256
            }
257
            else {
258
                $lead_in = sprintf($output_mod->trans('On %s, somebody said')."\n\n", $headers['Date']);
2✔
259
            }
260
        }
261
    }
262
    elseif ($type == 'forward') {
1✔
263
        $flds = array();
1✔
264
        foreach( array('From', 'Date', 'Subject', 'To', 'Cc') as $fld) {
1✔
265
            if (array_key_exists($fld, $headers)) {
1✔
266
                $flds[$fld] = $headers[$fld];
1✔
267
            }
268
        }
269
        $lead_in = "\n\n----- ".$output_mod->trans('begin forwarded message')." -----\n\n";
1✔
270
        foreach ($flds as $fld => $val) {
1✔
271
            $lead_in .= $fld.': '.$val."\n";
1✔
272
        }
273
        $lead_in .= "\n";
1✔
274
    }
275
    return $lead_in;
2✔
276
}}
277

278
/**
279
 * Format reply field details
280
 * @subpackage core/functions
281
 * @param array $headers message headers
282
 * @param string $body message body
283
 * @param string $lead_in body lead in text
284
 * @param string $reply_type type (forward, reply, reply_all)
285
 * @param array $struct message structure details
286
 * @param int $html set to 1 if the output should be HTML
287
 * @return array
288
 */
289
if (!hm_exists('reply_format_body')) {
187✔
290
function reply_format_body($headers, $body, $lead_in, $reply_type, $struct, $html) {
291
    $msg = '';
2✔
292
    $type = 'textplain';
2✔
293
    if (array_key_exists('type', $struct) && array_key_exists('subtype', $struct)) {
2✔
294
        $type = mb_strtolower($struct['type']).mb_strtolower($struct['subtype']);
2✔
295
    }
296
    if ($html == 1) {
2✔
297
        $msg = format_reply_as_html($body, $type, $reply_type, $lead_in);
1✔
298
    }
299
    else {
300
        $msg = format_reply_as_text($body, $type, $reply_type, $lead_in);
2✔
301
    }
302
    return $msg;
2✔
303
}}
304

305
/**
306
 * Format reply text as HTML
307
 * @subpackage core/functions
308
 * @param string $body message body
309
 * @param string $type MIME type
310
 * @param string $reply_type type (forward, reply, reply_all)
311
 * @param string $lead_in body lead in text
312
 * @return string
313
 */
314
if (!hm_exists('format_reply_as_html')) {
187✔
315
function format_reply_as_html($body, $type, $reply_type, $lead_in) {
316
    if ($type == 'textplain') {
2✔
317
        if ($reply_type == 'reply' || $reply_type == 'reply_all') {
2✔
318
            $msg = nl2br($lead_in.format_reply_text($body));
2✔
319
        }
320
        elseif ($reply_type == 'forward') {
1✔
321
            $msg = nl2br($lead_in.$body);
2✔
322
        }
323
    }
324
    elseif ($type == 'texthtml') {
1✔
325
        $msg = nl2br($lead_in).'<hr /><blockquote>'.format_msg_html($body).'</blockquote>';
1✔
326
    }
327
    return $msg;
2✔
328
}}
329

330
/**
331
 * Format reply text as text
332
 * @subpackage core/functions
333
 * @param string $body message body
334
 * @param string $type MIME type
335
 * @param string $reply_type type (forward, reply, reply_all)
336
 * @param string $lead_in body lead in text
337
 * @return string
338
 */
339
if (!hm_exists('format_reply_as_text')) {
187✔
340
function format_reply_as_text($body, $type, $reply_type, $lead_in) {
341
    $msg = '';
3✔
342
    if ($type == 'texthtml') {
3✔
343
        if ($reply_type == 'reply' || $reply_type == 'reply_all') {
1✔
344
            $msg = $lead_in.format_reply_text(convert_html_to_text($body));
1✔
345
        }
346
        elseif ($reply_type == 'forward') {
1✔
347
            $msg = $lead_in.convert_html_to_text($body);
1✔
348
        }
349
    }
350
    elseif ($type == 'textplain') {
3✔
351
        if ($reply_type == 'reply' || $reply_type == 'reply_all') {
3✔
352
            $msg = $lead_in.format_reply_text($body);
3✔
353
        }
354
        else {
355
            $msg = $lead_in.$body;
1✔
356
        }
357
    }
358
    return $msg;
3✔
359
}}
360

361
/**
362
 * Convert header keys to lowercase versions
363
 * @param array $headers message headers
364
 * @return array
365
 */
366
if (!hm_exists('lc_headers')) {
187✔
367
function lc_headers($headers) {
368
    return array_change_key_case($headers, CASE_LOWER);
3✔
369
}}
370

371
/**
372
 * Get the in-reply-to message id for replied
373
 * @subpackage core/functions
374
 * @param array $headers message headers
375
 * @param string $type reply type
376
 * @return string
377
 */
378
if (!hm_exists('reply_to_id')) {
187✔
379
function reply_to_id($headers, $type) {
380
    $id = '';
2✔
381
    $headers = lc_headers($headers);
2✔
382
    if ($type != 'forward' && array_key_exists('message-id', $headers)) {
2✔
383
        $id = $headers['message-id'];
1✔
384
    }
385
    return $id;
2✔
386
}}
387

388
/**
389
 * Get reply field details
390
 * @subpackage core/functions
391
 * @param string $body message body
392
 * @param array $headers message headers
393
 * @param array $struct message structure details
394
 * @param int $html set to 1 if the output should be HTML
395
 * @param string $type optional type (forward, reply, reply_all)
396
 * @param object $output_mod output module object
397
 * @param string $type the reply type
398
 * @return array
399
 */
400
if (!hm_exists('format_reply_fields')) {
187✔
401
function format_reply_fields($body, $headers, $struct, $html, $output_mod, $type='reply') {
402
    $msg_to = '';
1✔
403
    $msg = '';
1✔
404
    $subject = reply_to_subject($headers, $type);
1✔
405
    $msg_id = reply_to_id($headers, $type);
1✔
406
    list($msg_to, $msg_cc) = reply_to_address($headers, $type);
1✔
407
    $lead_in = reply_lead_in($headers, $type, $msg_to, $output_mod);
1✔
408
    $msg = reply_format_body($headers, $body, $lead_in, $type, $struct, $html);
1✔
409
    return array($msg_to, $msg_cc, $subject, $msg, $msg_id);
1✔
410
}}
411

412
/**
413
 * decode mail fields to human readable text
414
 * @param string $string field to decode
415
 * @return string decoded field
416
 */
417
if (!hm_exists('decode_fld')) {
187✔
418
function decode_fld($string) {
419
    if (mb_strpos($string, '=?') === false) {
1✔
420
        return $string;
1✔
421
    }
422
    $string = preg_replace("/\?=\s+=\?/", '?==?', $string);
1✔
423
    if (preg_match_all("/(=\?[^\?]+\?(q|b)\?[^\?]+\?=)/i", $string, $matches)) {
1✔
424
        foreach ($matches[1] as $v) {
1✔
425
            $fld = mb_substr($v, 2, -2);
1✔
426
            $charset = mb_strtolower(mb_substr($fld, 0, mb_strpos($fld, '?')));
1✔
427
            $fld = mb_substr($fld, (mb_strlen($charset) + 1));
1✔
428
            $encoding = $fld[0];
1✔
429
            $fld = mb_substr($fld, (mb_strpos($fld, '?') + 1));
1✔
430
            if (mb_strtoupper($encoding) == 'B') {
1✔
431
                $fld = convert_to_utf8(base64_decode($fld), $charset);
1✔
432
            }
433
            elseif (mb_strtoupper($encoding) == 'Q') {
1✔
434
                $fld = convert_to_utf8(quoted_printable_decode(str_replace('_', ' ', $fld)), $charset);
1✔
435
            }
436
            $string = str_replace($v, $fld, $string);
1✔
437
        }
438
    }
439
    return trim($string);
1✔
440
}}
441

442
if (!hm_exists('convert_to_utf8')) {
187✔
443
function convert_to_utf8($data, $from_encoding) {
444
    try {
445
        $data = mb_convert_encoding($data, 'UTF-8', $from_encoding);
1✔
446
    } catch (ValueError $e) {
×
447
        $data = iconv($from_encoding, 'UTF-8', $data);
×
448
    }
449
    return $data;
1✔
450
}}
451

452
/**
453
 * @subpackage core/class
454
 */
455
class HTMLToText {
456

457
    public $text = '';
458
    private $current = false;
459
    private $blocks = array('table', 'li', 'div', 'h1', 'h2', 'br', 'h3', 'h4', 'h5', 'p', 'tr');
460
    private $skips = array('head', 'script', 'style');
461

462
    function __construct($html) {
2✔
463
        $doc = new DOMDocument();
2✔
464
        $doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
2✔
465
        if (trim($html) && $doc->hasChildNodes()) {
2✔
466
            $this->parse_nodes($doc->childNodes);
2✔
467
        }
468
        $this->text = trim(strip_tags(html_entity_decode(preg_replace("/\n{2,}/m", "\n\n", $this->text), ENT_QUOTES, "UTF-8")));
2✔
469
    }
470

471
    function block($tag) {
2✔
472
        in_array($tag, $this->blocks) && $this->current != $tag ? $this->text .= "\n" : false;
2✔
473
        $this->current = $tag;
2✔
474
    }
475

476
    function parse_nodes($nodes) {
2✔
477
        $trims = " \t\n\r\0\x0B";
2✔
478
        foreach ($nodes as $node) {
2✔
479
            if (!in_array($node->nodeName, $this->skips)) {
2✔
480
                $this->block($node->nodeName);
2✔
481
                if ($node->nodeName == '#text' && trim($node->textContent, $trims)) {
2✔
482
                    $this->text .= trim($node->textContent, $trims)." ";
2✔
483
                }
484
                $node->hasChildNodes() ? $this->parse_nodes($node->childNodes) : false;
2✔
485
            }
486
        }
487
    }
488
}
489

490
/**
491
 * trim a potential E-mail value
492
 * @param $val string E-mail value
493
 * @return string trimmed value
494
 */
495
if (!hm_exists('addr_split')) {
187✔
496
function trim_email($val) {
497
    $seps = array(',', ';');
3✔
498
    $misc = array('"', "'", '>', '<');
3✔
499
    return trim($val, implode(array_merge($misc, $seps)));
3✔
500
}}
501

502
/**
503
 * Split an address field
504
 * @param $str string field value
505
 * @param $seps array break chars
506
 * @return array results
507
 */
508
if (!hm_exists('addr_split')) {
187✔
509
function addr_split($str, $seps = array(',', ';')) {
510
    $str = preg_replace('/(\s){2,}/', ' ', $str);
4✔
511
    $max = mb_strlen($str);
4✔
512
    $word = '';
4✔
513
    $words = array();
4✔
514
    $capture = false;
4✔
515
    $capture_chars = array('"' => '"', '(' => ')', '<' => '>');
4✔
516
    for ($i=0;$i<$max;$i++) {
4✔
517
        $char = mb_substr($str, $i, 1);
4✔
518
        if ($capture && $capture_chars[$capture] == $char) {
4✔
519
            $capture = false;
2✔
520
        }
521
        elseif (!$capture && in_array($char, array_keys($capture_chars))) {
4✔
522
            $capture = $char;
2✔
523
        }
524

525
        if (!$capture && in_array($char, $seps)) {
4✔
526
            $words[] = trim($word);
2✔
527
            $word = '';
2✔
528
        }
529
        else {
530
            $word .= $char;
4✔
531
        }
532
    }
533
    $words[] = trim($word);
4✔
534
    return $words;
4✔
535
}}
536

537
/**
538
 * Parse an address field
539
 * @param $str string field value
540
 * @return array results
541
 */
542
if (!hm_exists('addr_parse')) {
187✔
543
function addr_parse($str) {
544
    $label = array();
3✔
545
    $email = '';
3✔
546
    $comment = array();
3✔
547
    foreach (addr_split($str, array(' ')) as $token) {
3✔
548
        if (is_email_address(trim_email($token))) {
3✔
549
            $email = trim_email($token);
3✔
550
        }
551
        else {
552
            $label[] = $token;
2✔
553
        }
554
    }
555
    $label = implode(' ', $label);
3✔
556
    if (preg_match('/\([^)]+\)/', $label, $matches)) {
3✔
557
        foreach ($matches as $match) {
1✔
558
            $comment[] = $match;
1✔
559
            $label = str_replace($match, '', $label);
1✔
560
        }
561
        $comment = implode(',', $comment);
1✔
562
    }
563
    else {
564
        $comment = '';
3✔
565
    }
566
    return array('email' => $email, 'label' => preg_replace('/[\pZ\pC]+/u', ' ', trim($label, ' \'"')), 'comment' => $comment);
3✔
567
}}
568

569
/**
570
 * Parse an address field
571
 * @param $fld string field value
572
 * @return array results
573
 */
574
if (!hm_exists('process_address_fld')) {
187✔
575
function process_address_fld($fld) {
576
    $res = array();
3✔
577
    $count = 0;
3✔
578
    $pre = false;
3✔
579
    foreach (addr_split($fld) as $str) {
3✔
580
        $addr = addr_parse($str);
3✔
581
        if ($addr['email']) {
3✔
582
            if ($pre) {
3✔
583
                $addr['label'] = $pre.' '.$addr['label'];
×
584
                $pre = false;
×
585
            }
586
            $res[$count] = $addr;
3✔
587
        }
588
        elseif ($addr['label']) {
1✔
589
            $pre = $addr['label'];
1✔
590
        }
591
        $count++;
3✔
592
    }
593
    return $res;
3✔
594
}}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc