1<?php
2
3namespace dokuwiki\plugin\swarmwebhook\webhooks;
4
5use DateTime;
6use dokuwiki\plugin\struct\meta\Schema;
7use dokuwiki\plugin\swarmwebhook\meta\Response;
8
9class IFTTT extends AbstractWebhook
10{
11    const IFTTT_TIME_FORMAT = 'F d, Y \a\t h:iA';
12
13    public function run($json)
14    {
15        global $conf, $INPUT;
16
17        if ($conf['allowdebug']) {
18            dbglog($_SERVER);
19        }
20
21        /** @var null|\helper_plugin_swarmwebhook $helper */
22        $helper = plugin_load('helper', 'swarmwebhook');
23        if (!$helper) {
24            http_status(422, 'swarmwebhook plugin not active at this server');
25            return;
26        }
27        if ($helper->getConf('service') !== 'IFTTT') {
28            http_status(422, 'This service is deactivated in the plugin configuration.');
29            return;
30        }
31
32        // check that we have helper
33        $webhookData = json_decode($json, true);
34
35        $verificationResult = $this->verifyRequest($webhookData);
36        if ($verificationResult !== true) {
37            http_status($verificationResult->code, $verificationResult->content);
38            return;
39        }
40
41        $ok = $this->handleWebhookPayload($webhookData, $json);
42
43        if ($ok !== true) {
44            http_status($verificationResult->code, $verificationResult->content);
45            return;
46        }
47
48        http_status(202);
49    }
50
51    /**
52     * @param array $webhookData
53     *
54     * @return true|Response
55     *
56     */
57    protected function verifyRequest(array $webhookData)
58    {
59        /** @var null|\helper_plugin_swarmwebhook $helper */
60        $helper = plugin_load('helper', 'swarmwebhook');
61        $storedSecret = $helper->getConf('hook secret');
62        if (empty($storedSecret)) {
63            return true;
64        }
65
66        if (empty($webhookData['secret'])) {
67            return new Response(401, 'Header X_HOOK_SECRET missing!');
68        }
69
70        if ($webhookData['secret'] !== $storedSecret) {
71            return new Response(403, 'Header X_HOOK_SECRET not identical with configured secret!');
72        }
73
74        return true;
75    }
76
77    /**
78     * @param array $webhookData
79     * @param string $json
80     *
81     * @return true|Response
82     */
83    protected function handleWebhookPayload(array $webhookData, $json)
84    {
85        $lookupData = $this->extractDataFromPayload($webhookData);
86        $lookupData['json'] = $json;
87        $lookupData['service'] = 'IFTTT';
88
89
90        /** @var \helper_plugin_swarmwebhook $helper */
91        $helper = plugin_load('helper', 'swarmwebhook');
92        try {
93            $schemas = Schema::getAll('lookup');
94            if (!in_array('swarm', $schemas)) {
95                $helper->createNewSwarmSchema();
96            }
97
98            $helper->deleteCheckinFromLookup($lookupData['checkinid']);
99            $helper->saveDataToLookup($lookupData);
100        } catch (\Exception $e) { // FIXME: catch more specific exceptions!
101            $errorMessage = $e->getMessage();
102            dbglog($errorMessage);
103            return new Response(500, $errorMessage);
104        }
105
106        return true;
107    }
108
109    /**
110     * Extract the data to be saved from the payload
111     *
112     * @param array $data
113     *
114     * @return array
115     */
116    protected function extractDataFromPayload(array $data)
117    {
118        $checkinID = $data['ts'];
119        $locationName = $data['VenueName'];
120
121        // gues time zone
122        $nowTS = time();
123
124        $dateTime = $this->parseTimeIntoDateTime($data['ts'], $nowTS);
125
126        $lookupData = [
127            'date' => $dateTime->format('Y-m-d'),
128            'time' => $dateTime->format(\DateTime::ATOM),
129            'checkinid' => $checkinID,
130            'locname' => $locationName,
131        ];
132        if (!empty($data['shout'])) {
133            $lookupData['shout'] = $data['shout'];
134        }
135        return $lookupData;
136    }
137
138    /**
139     * @param $timestring
140     * @param $nowTS
141     *
142     * @return \DateTime
143     */
144    protected function parseTimeIntoDateTime($timestring, $nowTS)
145    {
146        //May 25, 2018 at 04:32PM
147
148        $guessedTZOffset = $this->guessTZOffset($timestring, $nowTS);
149        $timeZone = new \DateTimeZone($guessedTZOffset);
150        $dateTime = DateTime::createFromFormat(self::IFTTT_TIME_FORMAT, $timestring, $timeZone);
151
152        if (!$dateTime) {
153            return new DateTime('now', $timeZone);
154        }
155
156        return $dateTime;
157    }
158
159    protected function guessTZOffset($timestring, $nowTS)
160    {
161        $dateTime = DateTime::createFromFormat(self::IFTTT_TIME_FORMAT, $timestring, new \DateTimeZone('+0000'));
162        if ($dateTime === false) {
163            dbglog(DateTime::getLastErrors());
164            $dateTime = new DateTime('now');
165        }
166        $guessedOffset = round(($dateTime->getTimestamp() - $nowTS)/3600)*100;
167        $sign = $guessedOffset > 0 ? '+' : '';
168
169        return $sign . (string)$guessedOffset;
170    }
171}
172