xref: /web-bugs/www/bug.php (revision d50077c2)
1<?php
2/* User interface for viewing and editing bug details */
3
4use App\Repository\BugRepository;
5use App\Repository\CommentRepository;
6use App\Repository\ObsoletePatchRepository;
7use App\Repository\PackageRepository;
8use App\Repository\PatchRepository;
9use App\Utils\Captcha;
10use App\Repository\PullRequestRepository;
11use App\Repository\ReasonRepository;
12
13// Obtain common includes
14require_once '../include/prepend.php';
15
16// Start session
17session_start();
18
19$obsoletePatchRepository = $container->get(ObsoletePatchRepository::class);
20$patchRepository = $container->get(PatchRepository::class);
21
22$email = null;
23
24// Handle preview
25if (isset($_REQUEST['id']) && $_REQUEST['id'] == 'preview') {
26    $bug_id = 'PREVIEW';
27    $bug = $_SESSION['bug_preview'];
28    $bug['submitted'] = time();
29    $bug['modified'] = null;
30    $bug['votes'] = 0;
31    $bug['assign'] = '';
32
33    if (!$bug) {
34        redirect('index.php');
35    }
36} else {
37    // Bailout early if no/invalid bug id is passed
38    if (empty($_REQUEST['id']) || !((int) $_REQUEST['id'])) {
39        redirect('index.php');
40    } else {
41        $bug_id = (int) $_REQUEST['id'];
42    }
43}
44
45// Init common variables
46$errors = [];
47
48// Set edit mode
49$edit = isset($_REQUEST['edit']) ? (int) $_REQUEST['edit'] : 0;
50
51// Authenticate
52bugs_authenticate($user, $pw, $logged_in, $user_flags);
53
54$is_trusted_developer = ($user_flags & BUGS_TRUSTED_DEV);
55$is_security_developer = ($user_flags & (BUGS_TRUSTED_DEV | BUGS_SECURITY_DEV));
56
57// Handle unsubscription
58if (isset($_GET['unsubscribe'])) {
59    $unsubcribe = (int) $_GET['unsubscribe'];
60
61    $hash = isset($_GET['t']) ? $_GET['t'] : false;
62
63    if (!$hash) {
64        redirect("bug.php?id={$bug_id}");
65    }
66    unsubscribe($bug_id, $hash);
67    $_GET['thanks'] = 9;
68}
69
70// Subscription / Unsubscription
71if (isset($_POST['subscribe_to_bug']) || isset($_POST['unsubscribe_to_bug'])) {
72
73    // Check if session answer is set, then compare it with the post captcha value.
74    // If it's not the same, then it's an incorrect password.
75    if (!$logged_in) {
76        if (!isset($_SESSION['answer'])) {
77            $errors[] = 'Please enable cookies so the Captcha system can work';
78        } elseif ($_POST['captcha'] != $_SESSION['answer']) {
79            $errors[] = 'Incorrect Captcha';
80        }
81    }
82
83    if (empty($errors)) {
84        if ($logged_in && !empty($auth_user->email)) {
85            $email = $auth_user->email;
86        } else {
87            $email = isset($_POST['in']['commentemail']) ? $_POST['in']['commentemail'] : '';
88        }
89        if ($email == '' || !is_valid_email($email, $logged_in)) {
90            $errors[] = 'You must provide a valid email address.';
91        } else {
92            // Unsubscribe
93            if (isset($_POST['unsubscribe_to_bug'])) {
94                // Generate the hash
95                unsubscribe_hash($bug_id, $email);
96                $thanks = 8;
97            }
98            else // Subscribe
99            {
100                $dbh->prepare('REPLACE INTO bugdb_subscribe SET bug_id = ?, email = ?')->execute([$bug_id, $email]);
101                $thanks = 7;
102            }
103            redirect("bug.php?id={$bug_id}&thanks={$thanks}");
104        }
105    }
106    // If we get here, display errors
107    response_header('Error in subscription');
108    display_bug_error($errors);
109    response_footer();
110    exit;
111}
112
113// Delete comment
114if ($edit == 1 && $is_trusted_developer && isset($_GET['delete_comment'])) {
115    $delete_comment = (int) $_GET['delete_comment'];
116    $addon = '';
117
118    if ($delete_comment) {
119        delete_comment($bug_id, $delete_comment);
120        $addon = '&thanks=1';
121    }
122    redirect("bug.php?id=$bug_id&edit=1$addon");
123}
124
125// captcha is not necessary if the user is logged in
126if (!$logged_in) {
127    $captcha = $container->get(Captcha::class);
128}
129
130$trytoforce = isset($_POST['trytoforce']) ? (int) $_POST['trytoforce'] : 0;
131
132// fetch info about the bug into $bug
133if (!isset($bug)) {
134    $bugRepository = $container->get(BugRepository::class);
135    $bug = $bugRepository->findOneById($bug_id);
136}
137
138// DB error
139if (is_object($bug)) {
140    response_header('DB error');
141    display_bug_error($bug);
142    response_footer();
143    exit;
144}
145
146// Bug not found with passed id
147if (!$bug) {
148    response_header('No Such Bug');
149    display_bug_error("No such bug #{$bug_id}");
150    response_footer();
151    exit;
152}
153
154$show_bug_info = bugs_has_access($bug_id, $bug, $pw, $user_flags);
155if ($edit == 2 && !$show_bug_info && $pw && verify_bug_passwd($bug_id, bugs_get_hash($pw))) {
156    $show_bug_info = true;
157}
158
159if (isset($_POST['ncomment'])) {
160    /* Bugs blocked to user comments can only be commented by the team */
161    if ($bug['block_user_comment'] == 'Y' && $logged_in != 'developer') {
162        response_header('Adding comments not allowed');
163        display_bug_error("You're not allowed to add a comment to bug #{$bug_id}");
164        response_footer();
165        exit;
166    }
167}
168
169/* Just developers can change private/block_user_comment options */
170if (!empty($_POST['in'])) {
171    if ($user_flags & BUGS_DEV_USER) {
172        $block_user = isset($_POST['in']['block_user_comment']) ? 'Y' : 'N';
173    }
174    if ($is_security_developer) {
175        $is_private = isset($_POST['in']['private']) ? 'Y': 'N';
176    }
177}
178
179$block_user = isset($block_user) ? $block_user : $bug['block_user_comment'];
180$is_private = isset($is_private) ? $is_private : $bug['private'];
181
182// Handle any updates, displaying errors if there were any
183$RESOLVE_REASONS = $FIX_VARIATIONS = $pseudo_pkgs = [];
184
185$project = $bug['project'];
186
187// Only fetch stuff when it's really needed
188if ($edit && $edit < 3) {
189    $packageRepository = $container->get(PackageRepository::class);
190    $pseudo_pkgs = $packageRepository->findEnabled();
191}
192
193// Fetch RESOLVE_REASONS array
194if ($edit === 1) {
195    $reasonRepository = $container->get(ReasonRepository::class);
196    list($RESOLVE_REASONS, $FIX_VARIATIONS) = $reasonRepository->findByProject($project);
197}
198
199if (isset($_POST['ncomment']) && !isset($_POST['preview']) && $edit == 3) {
200    // Submission of additional comment by others
201
202    // Bug is private (just should be available to trusted developers and to reporter)
203    if (!$is_security_developer && $bug['private'] == 'Y') {
204        response_header('Private report');
205        display_bug_error("The bug #{$bug_id} is not available to public, if you are the original reporter use the Edit tab");
206        response_footer();
207        exit;
208    }
209
210    // Check if session answer is set, then compare it with the post captcha value.
211    // If it's not the same, then it's an incorrect password.
212    if (!$logged_in) {
213        if (!isset($_SESSION['answer'])) {
214            $errors[] = 'Please enable cookies so the Captcha system can work';
215        } elseif ($_POST['captcha'] != $_SESSION['answer']) {
216            $errors[] = 'Incorrect Captcha';
217        }
218    }
219
220    $ncomment = trim($_POST['ncomment']);
221    if (!$ncomment) {
222        $errors[] = 'You must provide a comment.';
223    }
224
225    // primitive spam detection
226    if ($message = is_spam($ncomment)) {
227        $errors[] = $message;
228    }
229    if (is_spam($_POST['in']['commentemail'])) {
230        $errors[] = "Please do not SPAM our bug system.";
231    }
232    if (is_spam_user($_POST['in']['commentemail'])) {
233        $errors[] = "Please do not SPAM our bug system.";
234    }
235
236    if (!$errors) {
237        do {
238            if (!$logged_in) {
239
240                if (!is_valid_email($_POST['in']['commentemail'], $logged_in)) {
241                    $errors[] = 'You must provide a valid email address.';
242                    response_header('Add Comment - Problems');
243                    break; // skip bug comment addition
244                }
245
246                $_POST['in']['name'] = '';
247            } else {
248                $_POST['in']['commentemail'] = $auth_user->email;
249                $_POST['in']['name'] = $auth_user->name;
250            }
251
252            $res = bugs_add_comment($bug_id, $_POST['in']['commentemail'], $_POST['in']['name'], $ncomment, 'comment');
253
254            mark_related_bugs($_POST['in']['commentemail'], $_POST['in']['name'], $ncomment);
255
256        } while (false);
257
258        $from = spam_protect($_POST['in']['commentemail'], 'text');
259    } else {
260        $from = '';
261    }
262} elseif (isset($_POST['ncomment']) && isset($_POST['preview']) && $edit == 3) {
263    $ncomment = trim($_POST['ncomment']);
264
265    // primitive spam detection
266    if ($message = is_spam($ncomment)) {
267        $errors[] = $message;
268    }
269
270    $from = $_POST['in']['commentemail'];
271    if (is_spam_user($from)) {
272        $errors[] = "Please do not SPAM our bug system.";
273    }
274
275} elseif (isset($_POST['in']) && !isset($_POST['preview']) && $edit == 2) {
276    // Edits submitted by original reporter for old bugs
277
278    if (!$show_bug_info || !verify_bug_passwd($bug_id, bugs_get_hash($pw))) {
279        $errors[] = 'The password you supplied was incorrect.';
280    }
281
282    // Bug is private (just should be available to trusted developers, original reporter and assigned dev)
283    if (!$show_bug_info && $bug['private'] == 'Y') {
284        response_header('Private report');
285        display_bug_error("The bug #{$bug_id} is not available to public");
286        response_footer();
287        exit;
288    }
289
290    // Just trusted dev can change the package name of a Security related bug to another package
291    if ($bug['private'] == 'Y' && !$is_security_developer
292        && $bug['bug_type'] == 'Security'
293        && $_POST['in']['bug_type'] != $bug['bug_type']) {
294
295        $errors[] = 'You cannot change the bug type of a Security bug!';
296    }
297
298    $ncomment = trim($_POST['ncomment']);
299    if (!$ncomment) {
300        $errors[] = 'You must provide a comment.';
301    }
302
303    // check that they aren't being bad and setting a status they aren't allowed to (oh, the horrors.)
304    if (isset($_POST['in']['status'])
305        && isset($state_types[$_POST['in']['status']])
306        && $_POST['in']['status'] != $bug['status'] && $state_types[$_POST['in']['status']] != 2) {
307        $errors[] = 'You aren\'t allowed to change a bug to that state.';
308    }
309
310    // check that they aren't changing the mail to a php.net address (gosh, somebody might be fooled!)
311    if (preg_match('/^(.+)@php\.net/i', $_POST['in']['email'], $m)) {
312        if ($user != $m[1] || $logged_in != 'developer') {
313            $errors[] = 'You have to be logged in as a developer to use your php.net email address.';
314            $errors[] = 'Tip: log in via another browser window then resubmit the form in this window.';
315        }
316    }
317
318    // primitive spam detection
319    if ($ncomment && $message = is_spam($ncomment)) {
320        $errors[] = $message;
321    }
322
323    if (!empty($_POST['in']['email']) &&
324        $bug['email'] != $_POST['in']['email']
325    ) {
326        $from = $_POST['in']['email'];
327    } else {
328        $from = $bug['email'];
329    }
330
331    if (is_spam_user($from)) {
332        $errors[] = "Please do not SPAM our bug system.";
333    }
334
335    if (!$errors && !($errors = incoming_details_are_valid($_POST['in'], false))) {
336        // Allow the reporter to change the bug type to 'Security', hence mark
337        // the report as private
338        if ($bug['private'] == 'N' && $_POST['in']['bug_type'] == 'Security'
339            && $_POST['in']['bug_type'] != $bug['bug_type']) {
340
341            $is_private = $_POST['in']['private'] = 'Y';
342        }
343
344        $dbh->prepare("
345            UPDATE bugdb
346            SET
347                sdesc = ?,
348                status = ?,
349                package_name = ?,
350                bug_type = ?,
351                php_version = ?,
352                php_os = ?,
353                email = ?,
354                ts2 = NOW(),
355                private = ?
356            WHERE id={$bug_id}
357        ")->execute([
358            $_POST['in']['sdesc'],
359            $_POST['in']['status'],
360            $_POST['in']['package_name'],
361            $_POST['in']['bug_type'],
362            $_POST['in']['php_version'],
363            $_POST['in']['php_os'],
364            $from,
365            $is_private
366        ]);
367
368        // Add changelog entry
369        $changed = bug_diff($bug, $_POST['in']);
370        if (!empty($changed)) {
371            $log_comment = bug_diff_render_html($changed);
372
373            if (!empty($log_comment)) {
374                $res = bugs_add_comment($bug_id, $from, '', $log_comment, 'log');
375            }
376        }
377
378        // Add normal comment
379        if (!empty($ncomment)) {
380            $res = bugs_add_comment($bug_id, $from, '', $ncomment, 'comment');
381
382            mark_related_bugs($from, '', $ncomment);
383        }
384    }
385} elseif (isset($_POST['in']) && isset($_POST['preview']) && $edit == 2) {
386    $ncomment = trim($_POST['ncomment']);
387    $from = isset($_POST['in']['commentemail']) ? $_POST['in']['commentemail'] : '';
388
389    // primitive spam detection
390    if ($message = is_spam($ncomment)) {
391        $errors[] = $message;
392    }
393    if (is_spam_user($from)) {
394        $errors[] = "Please do not SPAM our bug system.";
395    }
396
397} elseif (isset($_POST['in']) && is_array($_POST['in']) && !isset($_POST['preview']) && $edit == 1) {
398    // Edits submitted by developer
399
400    // Bug is private (just should be available to trusted developers, submitter and assigned dev)
401    if (!$show_bug_info && $bug['private'] == 'Y') {
402        response_header('Private report');
403        display_bug_error("The bug #{$bug_id} is not available to public");
404        response_footer();
405        exit;
406    }
407
408    if ($logged_in != 'developer') {
409        $errors[] = 'You have to login first in order to edit the bug report.';
410    }
411    $comment_name = $auth_user->name;
412    if (empty($_POST['ncomment'])) {
413        $ncomment = '';
414    } else {
415        $ncomment = trim($_POST['ncomment']);
416    }
417
418    // primitive spam detection
419    if ($ncomment && $message = is_spam($ncomment)) {
420        $errors[] = $message;
421    }
422
423    // Just trusted dev can set CVE-ID
424    if ($is_security_developer && !empty($_POST['in']['cve_id'])) {
425        // Remove the CVE- prefix
426        $_POST['in']['cve_id'] = preg_replace('/^\s*CVE-/i', '', $_POST['in']['cve_id']);
427    }
428    if (empty($_POST['in']['cve_id'])) {
429        $_POST['in']['cve_id'] = $bug['cve_id'];
430    }
431
432    if ($bug['private'] == 'N' && $bug['private'] != $is_private) {
433        if ($_POST['in']['bug_type'] != 'Security') {
434            $errors[] = 'Only Security bugs can be marked as private.';
435        }
436    }
437
438    global $state_types;
439    $allowed_state_types = array_filter($state_types, function ($var) {
440        return $var !== 0;
441    });
442    // Require comment for open bugs only
443    if (empty($_POST['in']['status']) || !isset($allowed_state_types[$_POST['in']['status']])) {
444        $errors[] = "You must provide a status";
445    } else {
446        if ($_POST['in']['status'] == 'Not a bug' &&
447            !in_array($bug['status'], ['Not a bug', 'Closed', 'Duplicate', 'No feedback', 'Wont fix']) &&
448            strlen(trim($ncomment)) == 0
449        ) {
450            $errors[] = "You must provide a comment when marking a bug 'Not a bug'";
451        } elseif (!empty($_POST['in']['resolve'])) {
452            if (!$trytoforce && isset($RESOLVE_REASONS[$_POST['in']['resolve']]) &&
453                $RESOLVE_REASONS[$_POST['in']['resolve']]['status'] == $bug['status'])
454            {
455                $errors[] = 'The bug is already marked "'.$bug['status'].'". (Submit again to ignore this.)';
456            } elseif (!$errors) {
457                if ($_POST['in']['status'] == $bug['status']) {
458                    $_POST['in']['status'] = $RESOLVE_REASONS[$_POST['in']['resolve']]['status'];
459                }
460                if (isset($FIX_VARIATIONS) && isset($FIX_VARIATIONS[$_POST['in']['resolve']][$bug['package_name']])) {
461                    $reason = $FIX_VARIATIONS[$_POST['in']['resolve']][$bug['package_name']];
462                } else {
463                    $reason = isset($RESOLVE_REASONS[$_POST['in']['resolve']]) ? $RESOLVE_REASONS[$_POST['in']['resolve']]['message'] : '';
464                }
465
466                // do a replacement on @svn@ to the likely location of SVN for this package
467                if ($_POST['in']['resolve'] == 'trysvn') {
468                    switch ($bug['package_name']) {
469                        case 'Documentation' :
470                        case 'Web Site' :
471                        case 'Bug System' :
472                        case 'PEPr' :
473                            $errors[] = 'Cannot use "try svn" with ' . $bug['package_name'];
474                            break;
475                        case 'PEAR' :
476                            $reason = str_replace('@svn@', 'pear-core', $reason);
477                            $ncomment = "$reason\n\n$ncomment";
478                            break;
479                        default :
480                            $reason = str_replace('@svn@', $bug['package_name'], $reason);
481                            $ncomment = "$reason\n\n$ncomment";
482                            break;
483                    }
484                } else {
485                    $ncomment = "$reason\n\n$ncomment";
486                }
487            }
488        }
489    }
490
491    $from = $auth_user->email;
492
493    if (!$errors && !($errors = incoming_details_are_valid($_POST['in']))) {
494        $query = 'UPDATE bugdb SET';
495
496        // Update email only if it's passed
497        if ($bug['email'] != $_POST['in']['email'] && !empty($_POST['in']['email'])) {
498            $query .= " email='{$_POST['in']['email']}',";
499        }
500
501        // Changing the package to 'Security related' should mark the bug as private automatically
502        if ($bug['bug_type'] != $_POST['in']['bug_type']) {
503            if ($_POST['in']['bug_type'] == 'Security' && $_POST['in']['status'] != 'Closed') {
504                $is_private = $_POST['in']['private'] = 'Y';
505            }
506        }
507
508        if ($logged_in != 'developer') {
509            // don't reset assigned status
510            $_POST['in']['assign'] = $bug['assign'];
511        }
512        if (!empty($_POST['in']['assign']) && $_POST['in']['status'] == 'Open') {
513            $status = 'Assigned';
514        } elseif (empty($_POST['in']['assign']) && $_POST['in']['status'] == 'Assigned') {
515            $status = 'Open';
516        } else {
517            $status = $_POST['in']['status'];
518        }
519
520        // Assign automatically when closed
521        if ($status == 'Closed' && $_POST['in']['assign'] == '') {
522            $_POST['in']['assign'] = $auth_user->handle;
523        }
524
525        $dbh->prepare($query . "
526                sdesc = ?,
527                status = ?,
528                package_name = ?,
529                bug_type = ?,
530                assign = ?,
531                php_version = ?,
532                php_os = ?,
533                block_user_comment = ?,
534                cve_id = ?,
535                private = ?,
536                ts2 = NOW()
537            WHERE id = {$bug_id}
538        ")->execute([
539            $_POST['in']['sdesc'],
540            $status,
541            $_POST['in']['package_name'],
542            $_POST['in']['bug_type'],
543            $_POST['in']['assign'],
544            $_POST['in']['php_version'],
545            $_POST['in']['php_os'],
546            $block_user,
547            $_POST['in']['cve_id'],
548            $is_private
549        ]);
550
551        // Add changelog entry
552        $changed = bug_diff($bug, $_POST['in']);
553        if (!empty($changed)) {
554            $log_comment = bug_diff_render_html($changed);
555
556            if (!empty($log_comment)) {
557                $res = bugs_add_comment($bug_id, $from, $comment_name, $log_comment, 'log');
558            }
559        }
560
561        // Add normal comment
562        if (!empty($ncomment)) {
563            $res = bugs_add_comment($bug_id, $from, $comment_name, $ncomment, 'comment');
564
565            mark_related_bugs($from, $comment_name, $ncomment);
566        }
567    }
568} elseif (isset($_POST['in']) && isset($_POST['preview']) && $edit == 1) {
569    $ncomment = trim($_POST['ncomment']);
570    $from = $auth_user->email;
571} elseif (isset($_POST['in'])) {
572    $errors[] = 'Invalid edit mode.';
573    $ncomment = '';
574} else {
575    $ncomment = '';
576}
577
578if (isset($_POST['in']) && !isset($_POST['preview']) && !$errors) {
579    mail_bug_updates($bug, $_POST['in'], $from, $ncomment, $edit, $bug_id);
580    redirect("bug.php?id=$bug_id&thanks=$edit");
581}
582
583switch (txfield('bug_type', $bug, isset($_POST['in']) ? $_POST['in'] : null))
584{
585    case 'Feature/Change Request':
586        $bug_type = 'Request';
587        break;
588    case 'Documentation Problem':
589        $bug_type = 'Doc Bug';
590        break;
591    case 'Security':
592        $bug_type = 'Sec Bug';
593        break;
594    default:
595    case 'Bug':
596        $bug_type = 'Bug';
597        break;
598}
599
600response_header(
601    $show_bug_info ? "{$bug_type} #{$bug_id} :: " . htmlspecialchars($bug['sdesc']) : "You must be logged in",
602    ($bug_id != 'PREVIEW') ? "
603        <link rel='alternate' type='application/rss+xml' title='{$bug['package_name']} Bug #{$bug['id']} - RDF' href='rss/bug.php?id={$bug_id}'>
604        <link rel='alternate' type='application/rss+xml' title='{$bug['package_name']} Bug #{$bug['id']} - RSS 2.0' href='rss/bug.php?id={$bug_id}&format=rss2'>
605    " : ''
606);
607
608// DISPLAY BUG
609$thanks = (isset($_GET['thanks'])) ? (int) $_GET['thanks'] : 0;
610switch ($thanks)
611{
612    case 1:
613    case 2:
614        echo '<div class="success">The bug was updated successfully.</div>';
615        break;
616    case 3:
617        echo '<div class="success">Your comment was added to the bug successfully.</div>';
618        break;
619    case 4:
620        $bug_url = "{$site_method}://{$site_url}{$basedir}/bug.php?id={$bug_id}";
621        echo '<div class="success">
622            Thank you for your help!
623            If the status of the bug report you submitted changes, you will be notified.
624            You may return here and check the status or update your report at any time.<br>
625            The URL for your bug report is: <a href="'.$bug_url.'">'.$bug_url.'</a>.
626            </div>';
627        break;
628    case 6:
629        echo '<div class="success">Thanks for voting! Your vote should be reflected in the statistics below.</div>';
630        break;
631    case 7:
632        echo '<div class="success">Your subscribe request has been processed.</div>';
633        break;
634    case 8:
635        echo '<div class="success">Your unsubscribe request has been processed, please check your email.</div>';
636        break;
637    case 9:
638        echo '<div class="success">You have successfully unsubscribed.</div>';
639        break;
640    case 10:
641        echo '<div class="success">Your vote has been updated.</div>';
642    break;
643
644    default:
645        break;
646}
647
648display_bug_error($errors);
649
650if (!$show_bug_info) {
651    echo '<div id="bugheader"></div>';
652} else{
653?>
654<div id="bugheader">
655    <table id="details">
656        <tr id="title">
657            <th class="details" id="number"><a href="bug.php?id=<?php echo $bug_id, '">', $bug_type , '</a>&nbsp;#' , $bug_id; ?></th>
658            <td id="summary" colspan="5"><?php echo htmlspecialchars($bug['sdesc']); ?></td>
659        </tr>
660        <tr id="submission">
661            <th class="details">Submitted:</th>
662            <td style="white-space: nowrap;"><?php echo format_date($bug['submitted']); ?></td>
663            <th class="details">Modified:</th>
664            <td style="white-space: nowrap;"><?php echo ($bug['modified']) ? format_date($bug['modified']) : '-'; ?></td>
665            <td rowspan="6">
666
667<?php    if ($bug['votes']) { ?>
668                <table id="votes">
669                    <tr><th class="details">Votes:</th><td><?php echo $bug['votes'] ?></td></tr>
670                    <tr><th class="details">Avg. Score:</th><td><?php printf("%.1f &plusmn; %.1f", $bug['average'], $bug['deviation']); ?></td></tr>
671                    <tr><th class="details">Reproduced:</th><td><?php printf("%d of %d (%.1f%%)", $bug['reproduced'], $bug['tried'], $bug['tried'] ? ($bug['reproduced'] / $bug['tried']) * 100 : 0); ?></td></tr>
672<?php        if ($bug['reproduced']) { ?>
673                    <tr><th class="details">Same Version:</th><td><?php printf("%d (%.1f%%)", $bug['samever'], ($bug['samever'] / $bug['reproduced']) * 100); ?></td></tr>
674                    <tr><th class="details">Same OS:</th><td><?php printf("%d (%.1f%%)", $bug['sameos'], ($bug['sameos'] / $bug['reproduced']) * 100); ?></td></tr>
675<?php        } ?>
676                </table>
677<?php    } ?>
678
679            </td>
680        </tr>
681
682        <tr id="submitter">
683            <th class="details">From:</th>
684            <td><?php echo ($bug['status'] !== 'Spam') ? spam_protect(htmlspecialchars($bug['email'])) : 'Hidden because of SPAM'; ?></td>
685            <th class="details">Assigned:</th>
686<?php if (!empty($bug['assign'])) { ?>
687            <td><a href="search.php?cmd=display&amp;assign=<?php echo urlencode($bug['assign']), '">', htmlspecialchars($bug['assign']); ?></a> (<a href="https://people.php.net/<?php echo urlencode($bug['assign']); ?>">profile</a>)</td>
688<?php } else { ?>
689            <td><?php echo htmlspecialchars($bug['assign']); ?></td>
690<?php } ?>
691        </tr>
692
693        <tr id="categorization">
694            <th class="details">Status:</th>
695            <td><?php echo htmlspecialchars($bug['status']); ?></td>
696            <th class="details">Package:</th>
697            <td><a href="search.php?cmd=display&amp;package_name[]=<?php echo urlencode($bug['package_name']), '">', htmlspecialchars($bug['package_name']); ?></a><?php echo $bug['project'] == 'pecl' ? ' (<a href="https://pecl.php.net/package/'. htmlspecialchars($bug['package_name']) . '" target="_blank">PECL</a>)' : ''; ?></td>
698        </tr>
699
700        <tr id="situation">
701            <th class="details">PHP Version:</th>
702            <td><?php echo htmlspecialchars($bug['php_version']); ?></td>
703            <th class="details">OS:</th>
704            <td><?php echo htmlspecialchars($bug['php_os']); ?></td>
705        </tr>
706
707        <tr id="private">
708            <th class="details">Private report:</th>
709            <td><?php echo $bug['private'] == 'Y' ? 'Yes' : 'No'; ?></td>
710            <th class="details">CVE-ID:</th>
711            <td><?php if (!empty($bug['cve_id'])) { printf('<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-%s" target="_blank">%1$s</a>', htmlspecialchars($bug['cve_id'])); } else { ?><em>None</em><?php } ?></td>
712        </tr>
713    </table>
714</div>
715
716<?php
717}
718
719if ($bug_id !== 'PREVIEW') {
720    echo '<div class="controls">', "\n",
721        control(0, 'View'),
722        ($bug['private'] == 'N' ? control(3, 'Add Comment') : ''),
723        control(1, 'Developer'),
724        (!$email || $bug['email'] == $email? control(2, 'Edit') : ''),
725        '</div>', "\n";
726?>
727<div class="clear"></div>
728
729<?php if ($show_bug_info && !$edit && canvote($thanks, $bug['status'])) { ?>
730<form id="vote" method="post" action="vote.php">
731    <div class="sect">
732        <fieldset>
733            <legend>Have you experienced this issue?</legend>
734            <div>
735                <input type="radio" id="rep-y" name="reproduced" value="1" onchange="show('canreproduce')"> <label for="rep-y">yes</label>
736                <input type="radio" id="rep-n" name="reproduced" value="0" onchange="hide('canreproduce')"> <label for="rep-n">no</label>
737                <input type="radio" id="rep-d" name="reproduced" value="2" onchange="hide('canreproduce')" checked="checked"> <label for="rep-d">don't know</label>
738            </div>
739        </fieldset>
740        <fieldset>
741            <legend>Rate the importance of this bug to you:</legend>
742            <div>
743                <label for="score-5">high</label>
744                <input type="radio" id="score-5" name="score" value="2">
745                <input type="radio" id="score-4" name="score" value="1">
746                <input type="radio" id="score-3" name="score" value="0" checked="checked">
747                <input type="radio" id="score-2" name="score" value="-1">
748                <input type="radio" id="score-1" name="score" value="-2">
749                <label for="score-1">low</label>
750            </div>
751        </fieldset>
752    </div>
753    <div id="canreproduce" class="sect" style="display: none">
754        <fieldset>
755            <legend>Are you using the same PHP version?</legend>
756            <div>
757                <input type="radio" id="ver-y" name="samever" value="1"> <label for="ver-y">yes</label>
758                <input type="radio" id="ver-n" name="samever" value="0" checked="checked"> <label for="ver-n">no</label>
759            </div>
760        </fieldset>
761        <fieldset>
762            <legend>Are you using the same operating system?</legend>
763            <div>
764                <input type="radio" id="os-y" name="sameos" value="1"> <label for="os-y">yes</label>
765                <input type="radio" id="os-n" name="sameos" value="0" checked="checked"> <label for="os-n">no</label>
766            </div>
767        </fieldset>
768    </div>
769    <div id="submit" class="sect">
770        <input type="hidden" name="id" value="<?php echo $bug_id?>">
771        <input type="submit" value="Vote">
772    </div>
773</form>
774<br clear="all">
775<?php    }
776
777} // if ($bug_id != 'PREVIEW') {
778
779//
780// FIXME! Do not wrap here either. Re-use the comment display function!
781//
782
783if (isset($_POST['preview']) && !empty($ncomment)) {
784    $preview = '<div class="comment">';
785    $preview .= "<strong>[" . format_date(time()) . "] ";
786    $preview .= spam_protect(htmlspecialchars($from));
787    $preview .= "</strong>\n<pre class=\"note\">";
788    $comment = wordwrap($ncomment, 72);
789    $preview .= make_ticket_links(addlinks($comment));
790    $preview .= "</pre>\n";
791    $preview .= '</div>';
792} else {
793    $preview = '';
794}
795
796if ($edit == 1 || $edit == 2) { ?>
797
798<form id="update" action="bug.php?id=<?php echo $bug_id; ?>&amp;edit=<?php echo $edit; ?>" method="post">
799
800<?php
801    if ($edit == 2) {
802?>
803        <div class="explain">
804        <?php if (!isset($_POST['in'])) { ?>
805            Welcome back! If you're the original bug submitter, here's
806            where you can edit the bug or add additional notes.<br>
807            If this is not your bug, you can
808            <a href="bug.php?id=<?php echo $bug_id; ?>&amp;edit=3">add a comment by following this link</a>.<br>
809            If this is your bug, but you forgot your password, <a href="bug-pwd-finder.php?id=<?php echo $bug_id; ?>">you can retrieve your password here</a>.<br>
810        <?php } ?>
811
812            <table>
813                <tr>
814                    <td class="details">Passw<span class="accesskey">o</span>rd:</td>
815                    <td><input type="password" name="pw" value="<?php echo htmlspecialchars($pw); ?>" size="10" maxlength="20" accesskey="o"></td>
816                    <?php if (!$show_bug_info) { ?>
817                    <td><input type="submit" value="Submit"></td>
818                    <?php } ?>
819                </tr>
820            </table>
821        </div>
822<?php
823    } elseif ($logged_in == 'developer') {
824?>
825        <div class="explain">
826            Welcome back, <?php echo $user; ?>!
827            (Not <?php echo $user; ?>? <a href="logout.php">Log out.</a>)
828        </div>
829<?php
830    } else {
831?>
832        <div class="explain">
833            Welcome! If you don't have a Git account, you can't do anything here.<br>
834            You can <a href="bug.php?id=<?php echo $bug_id; ?>&amp;edit=3">add a comment by following this link</a>
835            or if you reported this bug, you can <a href="bug.php?id=<?php echo $bug_id; ?>&amp;edit=2">edit this bug over here</a>.
836            <div class="details">
837                <label for="svnuser">php.net Username:</label>
838                <input type="text" id="svnuser" name="user" value="<?php echo htmlspecialchars($user); ?>" size="10" maxlength="20">
839                <label for="svnpw">php.net Password:</label>
840                <input type="password" id="svnpw" name="pw" value="<?php echo htmlspecialchars($pw); ?>" size="10">
841                <!--<label for="save">Remember:</label><input style="vertical-align:middle;" type="checkbox" id="save" name="save" <?php echo !empty($_POST['save']) ? 'checked="checked"' : ''; ?>>-->
842                <?php if (!$show_bug_info) { ?>
843                <input type="submit" value="Submit">
844                <?php } ?>
845            </div>
846        </div>
847<?php
848    }
849
850    echo $preview;
851?>
852    <table>
853
854<?php
855      if ($edit == 1 && $show_bug_info) { /* Developer Edit Form */
856        if (isset($RESOLVE_REASONS) && $RESOLVE_REASONS) {
857?>
858        <tr>
859            <th class="details"><label for="in" accesskey="c">Qui<span class="accesskey">c</span>k Fix:</label></th>
860            <td colspan="3">
861                <select name="in[resolve]" id="in">
862                    <?php show_reason_types((isset($_POST['in']) && isset($_POST['in']['resolve'])) ? $_POST['in']['resolve'] : -1, 1); ?>
863                </select>
864
865<?php      if (isset($_POST['in']) && !empty($_POST['in']['resolve'])) { ?>
866                <input type="hidden" name="trytoforce" value="1">
867<?php      } ?>
868
869                <small>(<a href="quick-fix-desc.php">description</a>)</small>
870            </td>
871        </tr>
872<?php
873        }
874
875        if ($is_security_developer) { ?>
876        <tr>
877            <th class="details">CVE-ID:</th>
878            <td colspan="3">
879                <input type="text" size="15" maxlength="15" name="in[cve_id]" value="<?php echo field('cve_id'); ?>" id="cve_id">
880            </td>
881        </tr>
882        <tr>
883            <th class="details"></th>
884            <td colspan="3">
885                <input type="checkbox" name="in[private]" value="Y" <?php print $is_private == 'Y' ? 'checked="checked"' : ''; ?>> Private report (Normal user should not see it)
886            </td>
887        </tr>
888<?php   } ?>
889        <tr>
890            <th class="details"></th>
891            <td colspan="3">
892                <input type="checkbox" name="in[block_user_comment]" value="Y" <?php print $block_user == 'Y' ? 'checked="checked"' : ''; ?>> Block user comment
893            </td>
894        </tr>
895<?php } ?>
896
897<?php if ($show_bug_info) { ?>
898
899        <tr>
900            <th class="details">Status:</th>
901            <td <?php echo (($edit != 1) ? 'colspan="3"' : '' ); ?>>
902                <select name="in[status]">
903                    <?php show_state_options(isset($_POST['in']) && isset($_POST['in']['status']) ? $_POST['in']['status'] : '', $edit, $bug['status'], $bug['assign']); ?>
904                </select>
905
906<?php if ($edit == 1) { ?>
907            </td>
908            <th class="details">Assign to:</th>
909            <td>
910                <input type="text" size="10" maxlength="16" name="in[assign]" value="<?php echo field('assign'); ?>" id="assigned_user">
911<?php } ?>
912
913                <input type="hidden" name="id" value="<?php echo $bug_id ?>">
914                <input type="hidden" name="edit" value="<?php echo $edit ?>">
915                <input type="submit" value="Submit">
916            </td>
917        </tr>
918        <tr>
919            <th class="details">Package:</th>
920            <td colspan="3">
921                <select name="in[package_name]">
922                    <?php show_package_options(isset($_POST['in']) && isset($_POST['in']['package_name']) ? $_POST['in']['package_name'] : '', 0, $bug['package_name']); ?>
923                </select>
924            </td>
925        </tr>
926        <tr>
927            <th class="details">Bug Type:</th>
928            <td colspan="3">
929                <select name="in[bug_type]">
930                    <?php show_type_options($bug['bug_type'], /* deprecated */ true); ?>
931                </select>
932            </td>
933        </tr>
934        <tr>
935            <th class="details">Summary:</th>
936            <td colspan="3">
937                <input type="text" size="60" maxlength="80" name="in[sdesc]" value="<?php echo ($bug['status'] !== 'Spam') ? field('sdesc') : 'Hidden because of SPAM'; ?>">
938            </td>
939        </tr>
940        <tr>
941            <th class="details">From:</th>
942            <td colspan="3">
943                <?php echo ($bug['status'] !== 'Spam') ? spam_protect(field('email')) : 'Hidden because of SPAM'; ?>
944            </td>
945        </tr>
946        <tr>
947            <th class="details">New email:</th>
948            <td colspan="3">
949                <input type="text" size="40" maxlength="40" name="in[email]" value="<?php echo isset($_POST['in']) && isset($_POST['in']['email']) ? htmlspecialchars($_POST['in']['email']) : ''; ?>">
950            </td>
951        </tr>
952        <tr>
953            <th class="details">PHP Version:</th>
954            <td><input type="text" size="20" maxlength="100" name="in[php_version]" value="<?php echo field('php_version'); ?>"></td>
955            <th class="details">OS:</th>
956            <td><input type="text" size="20" maxlength="32" name="in[php_os]" value="<?php echo field('php_os'); ?>"></td>
957        </tr>
958    </table>
959
960    <p style="margin-bottom: 0em;">
961        <label for="ncomment" accesskey="m"><b>New<?php if ($edit == 1) echo "/Additional"; ?> Co<span class="accesskey">m</span>ment:</b></label>
962    </p>
963    <?php
964    if ($bug['block_user_comment'] == 'Y' && $logged_in != 'developer') {
965        echo 'Further comment on this bug is unnecessary.';
966    } elseif ($bug['status'] === 'Spam' && $logged_in != 'developer') {
967        echo 'This bug has a SPAM status, so no additional comments are needed.';
968    } else {
969    ?>
970        <textarea cols="80" rows="8" name="ncomment" id="ncomment" wrap="soft"><?php echo htmlspecialchars($ncomment); ?></textarea>
971    <?php
972    }
973    ?>
974
975    <p style="margin-top: 0em">
976        <input type="submit" name="preview" value="Preview">&nbsp;<input type="submit" value="Submit">
977    </p>
978
979</form>
980
981<?php } // if ($show_bug_info)
982} // if ($edit == 1 || $edit == 2)
983?>
984
985<?php
986    if ($edit == 3 && $bug['private'] == 'N') {
987
988    if ($bug['status'] === 'Spam') {
989        echo 'This bug has a SPAM status, so no additional comments are needed.';
990        response_footer();
991        exit;
992    }
993
994?>
995
996    <form name="comment" id="comment" action="bug.php" method="post">
997
998<?php if ($logged_in) { ?>
999    <div class="explain">
1000        <h1>
1001            <a href="patch-add.php?bug_id=<?php echo $bug_id; ?>">Click Here to Submit a Patch</a>
1002            <input type="submit" name="subscribe_to_bug" value="Subscribe">
1003            <input type="submit" name="unsubscribe_to_bug" value="Unsubscribe">
1004        </h1>
1005    </div>
1006<?php } ?>
1007
1008<?php if (!isset($_POST['in'])) { ?>
1009
1010        <div class="explain">
1011            Anyone can comment on a bug. Have a simpler test case? Does it
1012            work for you on a different platform? Let us know!<br>
1013            Just going to say 'Me too!'? Don't clutter the database with that please
1014
1015<?php
1016            if (canvote($thanks, $bug['status'])) {
1017                echo ' &mdash; but make sure to <a href="bug.php?id=' , $bug_id , '">vote on the bug</a>';
1018            }
1019?>!
1020        </div>
1021
1022<?php }
1023
1024echo $preview;
1025
1026if (!$logged_in) {
1027    $_SESSION['answer'] = $captcha->getAnswer();
1028?>
1029    <table>
1030        <tr>
1031            <th class="details">Y<span class="accesskey">o</span>ur email address:<br><strong>MUST BE VALID</strong></th>
1032            <td class="form-input">
1033                <input type="text" size="40" maxlength="40" name="in[commentemail]" value="<?php echo isset($_POST['in']['commentemail']) ? htmlspecialchars($_POST['in']['commentemail'], ENT_COMPAT, 'UTF-8') : ''; ?>" accesskey="o">
1034            </td>
1035        </tr>
1036        <tr>
1037            <th>Solve the problem:<br><?php echo htmlspecialchars($captcha->getQuestion()); ?></th>
1038            <td class="form-input"><input type="text" name="captcha"></td>
1039        </tr>
1040        <tr>
1041            <th class="details">Subscribe to this entry?</th>
1042            <td class="form-input">
1043                <input type="submit" name="subscribe_to_bug" value="Subscribe">
1044                <input type="submit" name="unsubscribe_to_bug" value="Unsubscribe">
1045            </td>
1046        </tr>
1047    </table>
1048</div>
1049<?php } ?>
1050
1051    <div>
1052        <input type="hidden" name="id" value="<?php echo $bug_id; ?>">
1053        <input type="hidden" name="edit" value="<?php echo $edit; ?>">
1054
1055    <?php
1056    if ($bug['block_user_comment'] == 'Y' && $logged_in != 'developer') {
1057        echo 'Further comment on this bug is unnecessary.';
1058    } elseif ($bug['status'] === 'Spam' && $logged_in != 'developer') {
1059        echo 'This bug has a SPAM status, so no additional comments are needed.';
1060    } else {
1061    ?>
1062        <textarea cols="80" rows="10" name="ncomment" wrap="soft"><?php echo htmlspecialchars($ncomment); ?></textarea>
1063    <?php
1064    }
1065    ?>
1066
1067        <br><input type="submit" name="preview" value="Preview">&nbsp;<input type="submit" value="Submit">
1068    </div>
1069
1070    </form>
1071
1072<?php } ?>
1073
1074<?php
1075
1076// Display original report
1077if ($bug['ldesc']) {
1078    if (!$show_bug_info) {
1079        echo 'This bug report is marked as private.';
1080    } else if ($bug['status'] !== 'Spam') {
1081        output_note(0, $bug['submitted'], $bug['email'], $bug['ldesc'], 'comment', $bug['reporter_name'], false);
1082    } else {
1083        echo 'The original report has been hidden, due to the SPAM status.';
1084    }
1085}
1086
1087// Display patches
1088if ($show_bug_info && $bug_id != 'PREVIEW' && $bug['status'] !== 'Spam') {
1089    $p = $patchRepository->findAllByBugId($bug_id);
1090    $revs = [];
1091    echo "<h2>Patches</h2>\n";
1092
1093    foreach ($p as $patch) {
1094        $revs[$patch['patch']][] = [$patch['revision'], $patch['developer']];
1095    }
1096
1097    foreach ($revs as $name => $revisions)
1098    {
1099        $obsolete = $obsoletePatchRepository->findObsoletingPatches($bug_id, $name, $revisions[0][0]);
1100        $style = !empty($obsolete) ? ' style="background-color: yellow; text-decoration: line-through;" ' : '';
1101        $url_name = urlencode($name);
1102        $clean_name = clean($name);
1103        $formatted_date = format_date($revisions[0][0]);
1104        $submitter = spam_protect($revisions[0][1]);
1105
1106        echo <<< OUTPUT
1107<a href="patch-display.php?bug_id={$bug_id}&amp;patch={$url_name}&amp;revision=latest" {$style}>{$clean_name}</a>
1108(last revision {$formatted_date} by {$submitter})
1109<br>
1110OUTPUT;
1111    }
1112    echo "<p><a href='patch-add.php?bug_id={$bug_id}'>Add a Patch</a></p>";
1113
1114    $pullRequestRepository = $container->get(PullRequestRepository::class);
1115    $pulls = $pullRequestRepository->findAllByBugId($bug_id);
1116    echo "<h2>Pull Requests</h2>\n";
1117
1118    require "{$ROOT_DIR}/templates/listpulls.php";
1119    echo "<p><a href='gh-pull-add.php?bug_id={$bug_id}'>Add a Pull Request</a></p>";
1120}
1121
1122// Display comments
1123$commentRepository = $container->get(CommentRepository::class);
1124$bug_comments = is_int($bug_id) ? $commentRepository->findByBugId($bug_id) : [];
1125
1126if ($show_bug_info && is_array($bug_comments) && count($bug_comments) && $bug['status'] !== 'Spam') {
1127    $history_tabs = [
1128        'type_all'     => 'All',
1129        'type_comment' => 'Comments',
1130        'type_log'     => 'Changes',
1131        'type_svn'     => 'Git/SVN commits',
1132        'type_related' => 'Related reports'
1133    ];
1134
1135    if (!isset($_COOKIE['history_tab']) || !isset($history_tabs[$_COOKIE['history_tab']])) {
1136        $active_history_tab = 'type_all';
1137    } else {
1138        $active_history_tab = $_COOKIE['history_tab'];
1139    }
1140    echo '<h2 style="border-bottom:2px solid #666;margin-bottom:0;padding:5px 0;">History</h2>',
1141            "<div id='comment_filter' class='controls comments'>";
1142
1143    foreach ($history_tabs as $id => $label)
1144    {
1145        $class_extra = ($id == $active_history_tab) ? 'active' : '';
1146        echo "<span id='{$id}' class='control {$class_extra}' onclick='do_comment(this);'>{$label}</span>";
1147    }
1148
1149    echo '            </div>
1150            ';
1151
1152    echo "<div id='comments_view' style='clear:both;'>\n";
1153    foreach ($bug_comments as $row) {
1154        output_note($row['id'], $row['added'], $row['email'], $row['comment'], $row['comment_type'], $row['comment_name'], !($active_history_tab == 'type_all' || ('type_' . $row['comment_type']) == $active_history_tab));
1155    }
1156    echo "</div>\n";
1157}
1158
1159if ($bug_id == 'PREVIEW') {
1160?>
1161
1162<form action="report.php?package=<?php htmlspecialchars($_SESSION['bug_preview']['package_name']); ?>" method="post">
1163<?php foreach($_SESSION['bug_preview'] as $k => $v) {
1164    if ($k !== 'ldesc') {
1165        if ($k === 'ldesc_orig') {
1166            $k = 'ldesc';
1167        }
1168        echo "<input type='hidden' name='in[", htmlspecialchars($k, ENT_QUOTES), "]' value='", htmlentities($v, ENT_QUOTES, 'UTF-8'), "'>";
1169    }
1170}
1171    echo "<input type='hidden' name='captcha' value='", htmlspecialchars($_SESSION['captcha'], ENT_QUOTES), "'>";
1172?>
1173    <input type='submit' value='Send bug report'> <input type='submit' name='edit_after_preview' value='Edit'>
1174</form>
1175
1176<?php }
1177
1178$bug_JS = <<< bug_JS
1179<script src='js/util.js'></script>
1180<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
1181<script src="js/jquery.cookie.js"></script>
1182<script>
1183function do_comment(nd)
1184{
1185    $('#comment_filter > .control.active').removeClass("active");
1186    $(nd).addClass("active");
1187
1188    $.cookie('history_tab', nd.id, { expires: 365 });
1189
1190    if (nd.id == 'type_all') {
1191        $('#comments_view > .comment:hidden').show('slow');
1192    } else {
1193        $('#comments_view > .comment').each(function(i) {
1194            if ($(this).hasClass(nd.id)) {
1195                $(this).show('slow');
1196            } else {
1197                $(this).hide('slow');
1198            }
1199        });
1200    }
1201    return false;
1202}
1203</script>
1204bug_JS;
1205
1206if ($edit == 1) {
1207    $bug_JS .= '
1208<script src="js/jquery.autocomplete-min.js"></script>
1209<script src="js/userlisting.php"></script>
1210<script src="js/search.js"></script>
1211    ';
1212
1213}
1214
1215response_footer($bug_JS);
1216
1217// Helper functions
1218
1219function mark_related_bugs($from, $comment_name, $ncomment)
1220{
1221    global $bug_id;
1222
1223    $related = get_ticket_links($ncomment);
1224
1225    /**
1226     * Adds a new comment on the related bug pointing to the current report
1227     */
1228    foreach ($related as $bug) {
1229        bugs_add_comment($bug, $from, $comment_name,
1230            'Related To: Bug #'. $bug_id, 'related');
1231    }
1232}
1233
1234function link_to_people($email, $text)
1235{
1236    $domain = strstr($email, "@");
1237    if ($domain == "@php.net") {
1238        $username = strstr($email, "@", true);
1239        return '<a href="//people.php.net/' . urlencode($username) . '">' . $text . '</a>';
1240    }
1241    return $text;
1242}
1243
1244function output_note($com_id, $ts, $email, $comment, $comment_type, $comment_name, $is_hidden = false)
1245{
1246    global $edit, $bug_id, $dbh, $is_trusted_developer, $logged_in;
1247
1248    $display = (!$is_hidden) ? '' : 'style="display:none;"';
1249
1250    echo "<div class='comment type_{$comment_type}' {$display}>";
1251    echo '<a name="' , urlencode($ts) , '">&nbsp;</a>';
1252    echo "<strong>[" , format_date($ts) , "] ";
1253    echo link_to_people($email, spam_protect(htmlspecialchars($email))) , "</strong>\n";
1254
1255    switch ($comment_type)
1256    {
1257        case 'log':
1258            echo "<div class='log_note'>{$comment}</div>";
1259            break;
1260
1261        default:
1262            // Delete comment action only for trusted developers
1263            echo ($edit == 1 && $com_id !== 0 && $is_trusted_developer) ? "<a href='bug.php?id={$bug_id}&amp;edit=1&amp;delete_comment={$com_id}'>[delete]</a>\n" : '';
1264
1265            $comment = make_ticket_links(addlinks($comment));
1266            echo "<pre class='note'>{$comment}\n</pre>\n";
1267    }
1268
1269    echo '</div>';
1270}
1271
1272function delete_comment($bug_id, $com_id)
1273{
1274    global $dbh;
1275
1276    $dbh->prepare("DELETE FROM bugdb_comments WHERE bug='{$bug_id}' AND id='{$com_id}'")->execute();
1277}
1278
1279function control($num, $desc)
1280{
1281    global $bug_id, $edit;
1282
1283    $str = "<span id='control_{$num}' class='control";
1284    if ($edit == $num) {
1285        $str .= " active'>{$desc}";
1286    } else {
1287        $str .= "'><a href='bug.php?id={$bug_id}" . (($num) ? "&amp;edit={$num}" : '') . "'>{$desc}</a>";
1288    }
1289    return "{$str}</span>\n";
1290}
1291
1292function canvote($thanks, $status)
1293{
1294    return ($thanks != 4 && $thanks != 6 && $status != 'Closed' && $status != 'Not a bug' && $status != 'Duplicate');
1295}
1296