1<?php
2
3/**
4 * DokuWiki Plugin sentry (Action Component)
5 *
6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
7 * @author  Andreas Gohr <dokuwiki@cosmocode.de>
8 */
9class action_plugin_sentry_ajax extends DokuWiki_Action_Plugin
10{
11
12    /**
13     * Registers a callback function for a given event
14     *
15     * @param Doku_Event_Handler $controller DokuWiki's event controller object
16     *
17     * @return void
18     */
19    public function register(Doku_Event_Handler $controller)
20    {
21        if (!$this->getConf('dsn')) {
22            return;
23        }
24        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax');
25    }
26
27    /**
28     *
29     * This uses event AJAX_CALL_UNKNOWN
30     *
31     * @param Doku_Event $event
32     * @param            $param
33     */
34    public function handleAjax(Doku_Event $event, $param)
35    {
36        if ($event->data !== 'plugin_sentry') {
37            return;
38        }
39        $event->preventDefault();
40        $event->stopPropagation();
41
42        global $INPUT;
43
44        $sentryData = [
45            'logger' => 'javascript',
46            'exception' => [
47                'values' => [
48                    [
49                        'type' => $INPUT->str('name'),
50                        'value' => $INPUT->str('message'),
51                        'stacktrace' => [
52                            'frames' => $this->parseJavaScriptStacktrace($INPUT->str('stack'))
53                        ],
54                    ],
55                ],
56            ],
57        ];
58        $sentryData = array_merge($sentryData, $INPUT->arr('additionalData'));
59        $sentryData['extra']['original_stack'] = $INPUT->str('stack');
60
61        $sentryEvent = new \dokuwiki\plugin\sentry\Event($sentryData);
62
63        /** @var helper_plugin_sentry $sentryHelper */
64        $sentryHelper = plugin_load('helper', 'sentry');
65        $sentryHelper->logEvent($sentryEvent);
66    }
67
68    /**
69     * Tries to parse a JavaScript stack trace into sentry frames
70     *
71     * @see https://github.com/errwischt/stacktrace-parser/blob/master/lib/stacktrace-parser.js
72     * @param string $trace
73     * @return array
74     */
75    protected function parseJavaScriptStacktrace($trace)
76    {
77        $chrome = '/^\s*at (?:(?:(?:Anonymous function)?|((?:\[object object\])?(?:new )?\S+'.
78            '(?: \[as \S+\])?)) )?\(?((?:[-\w]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i';
79
80        $gecko = '/^(?:\s*([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i';
81
82        $frames = [];
83        $lines = explode("\n", $trace);
84        foreach ($lines as $line) {
85            if (preg_match($gecko, $line, $parts)) {
86                $frames[] = [
87                    'filename' => $parts[3] ? $parts[3] : '<unknown file>',
88                    'function' => $parts[1] ? $parts[1] : '<unknown function>',
89                    'lineno' => (int)$parts[4],
90                    'colno' => (int)$parts[5]
91                ];
92            } elseif (preg_match($chrome, $line, $parts)) {
93                $frames[] = [
94                    'filename' => $parts[2] ? $parts[2] : '<unknown file>',
95                    'function' => $parts[1] ? $parts[1] : '<unknown function>',
96                    'lineno' => (int)$parts[3],
97                    'colno' => (int)$parts[4]
98                ];
99            }
100        }
101        return array_reverse($frames);
102    }
103}
104