Passed
Push — master ( 2f8638...569786 )
by Dominik
25:17
created

removeEmptyArrayElements()   A

Complexity

Conditions 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
c 0
b 0
f 0
nop 1
dl 0
loc 13
rs 10
1
<?php
2
3
namespace Azine\MailgunWebhooksBundle\Controller;
4
5
use Azine\MailgunWebhooksBundle\DependencyInjection\AzineMailgunWebhooksExtension;
6
use Azine\MailgunWebhooksBundle\Entity\MailgunAttachment;
7
use Azine\MailgunWebhooksBundle\Entity\MailgunCustomVariable;
8
use Azine\MailgunWebhooksBundle\Entity\MailgunEvent;
9
use Azine\MailgunWebhooksBundle\Entity\MailgunMessageSummary;
10
use Azine\MailgunWebhooksBundle\Entity\MailgunWebhookEvent;
11
use Azine\MailgunWebhooksBundle\Entity\Repositories\MailgunEventRepository;
12
use Psr\Log\LoggerInterface;
13
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
14
use Symfony\Component\HttpFoundation\File\UploadedFile;
15
use Symfony\Component\HttpFoundation\JsonResponse;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
19
/**
20
 * MailgunEvent controller.
21
 */
22
class MailgunWebhookController extends AbstractController
23
{
24
    public function createFromWebhookAction(Request $request)
25
    {
26
        // old webhooks api
27
        $params = $request->request->all();
28
29
        if (is_array($params) && !empty($params)) {
30
            $this->get("logger")->info("Creating MailgunEvent via old API.");
0 ignored issues
show
Bug introduced by
The method info() does not exist on stdClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

30
            $this->get("logger")->/** @scrutinizer ignore-call */ info("Creating MailgunEvent via old API.");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
31
            return $this->createEventOldApi($params);
32
        }
33
        // new webhooks api
34
        $this->get("logger")->info("Creating MailgunEvent via new API.");
35
        $params = json_decode($request->getContent(), true);
36
37
        return $this->createEventNewApi($params);
38
    }
39
40
    private function createEventNewApi($paramsPre)
41
    {
42
        $params = array_change_key_case($paramsPre, CASE_LOWER);
43
44
        if (sizeof($params) != sizeof($paramsPre)) {
45
            $params['params_contained_duplicate_keys'] = $paramsPre;
46
        }
47
48
        /////////////////////////////////////////////////////
49
        // signature validation
50
        $signatureData = $params['signature'];
51
        $eventData = $params['event-data'];
52
53
        // check if the timestamp is fresh
54
        $timestamp = $signatureData['timestamp'];
55
        $tsAge = abs(time() - $timestamp);
56
        if ($tsAge > 15) {
57
            return new Response('Signature verification failed. Timestamp too old abs('.time()." - $timestamp) = $tsAge", 401);
58
        }
59
60
        // validate post-data
61
        $key = $this->container->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
0 ignored issues
show
Bug introduced by
The method getParameter() does not exist on Psr\Container\ContainerInterface. It seems like you code against a sub-type of Psr\Container\ContainerInterface such as Symfony\Component\Depend...tion\ContainerInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

61
        /** @scrutinizer ignore-call */ 
62
        $key = $this->container->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
Loading history...
62
        $token = $signatureData['token'];
63
        $expectedSignature = hash_hmac('SHA256', $timestamp.$token, $key);
64
        if ($expectedSignature != $signatureData['signature']) {
65
            return new Response('Signature verification failed.', 401);
66
        }
67
68
        /////////////////////////////////////////////////////
69
        // create event-entity
70
        try {
71
            // create event & populate with supplied data
72
            $event = new MailgunEvent();
73
74
            // token
75
            if (array_key_exists('token', $signatureData)) {
76
                $event->setToken($signatureData['token']);
77
                unset($signatureData['token']);
78
            }
79
            // timestamp
80
            if (array_key_exists('timestamp', $signatureData)) {
81
                $event->setTimestamp($signatureData['timestamp']);
82
                unset($signatureData['timestamp']);
83
            }
84
            // signature
85
            if (array_key_exists('signature', $signatureData)) {
86
                $event->setSignature($signatureData['signature']);
87
                unset($signatureData['signature']);
88
            }
89
90
            // event
91
            if (array_key_exists('event', $eventData)) {
92
                $event->setEvent($eventData['event']);
93
                unset($eventData['event']);
94
            }
95
            // domain
96
            if (array_key_exists('envelope', $eventData)) {
97
                $envelope = $eventData['envelope'];
98
                $sender = $envelope['sender'];
99
                $event->setDomain(substr($sender, strrpos($sender, '@') + 1));
100
101
                // ip
102
                if (array_key_exists('sending-ip', $envelope)) {
103
                    $event->setIp($envelope['sending-ip']);
104
                    unset($eventData['envelope']['sending-ip']);
105
                }
106
107
                unset($eventData['envelope']['sender']);
108
            }
109
            // description & reason
110
            if (array_key_exists('delivery-status', $eventData)) {
111
                $description = array_key_exists('message', $eventData['delivery-status']) ? $eventData['delivery-status']['message'].' ' : '';
112
                $description .= array_key_exists('description', $eventData['delivery-status']) ? $eventData['delivery-status']['description'] : '';
113
114
                $event->setDescription($description);
115
                unset($eventData['delivery-status']['message'], $eventData['delivery-status']['description']);
116
117
                // delivery status code
118
                if (array_key_exists('code', $eventData['delivery-status'])) {
119
                    $event->setErrorCode($eventData['delivery-status']['code']);
120
                    unset($eventData['delivery-status']['code']);
121
                }
122
            } elseif (array_key_exists('reject', $eventData)) {
123
                $description = array_key_exists('description', $eventData['reject']) ? $eventData['reject']['description'] : '';
124
                $reason = array_key_exists('reason', $eventData['reject']) ? $eventData['reject']['reason'] : '';
125
                $event->setDescription($description);
126
                $event->setReason($reason);
127
                unset($eventData['delivery-status']['description'], $eventData['delivery-status']['reason']);
128
            }
129
            // reason
130
            if (array_key_exists('reason', $eventData)) {
131
                $event->setReason($eventData['reason']);
132
                unset($eventData['reason']);
133
            }
134
            // recipient
135
            if (array_key_exists('recipient', $eventData)) {
136
                $event->setRecipient($eventData['recipient']);
137
                unset($eventData['recipient']);
138
            }
139
            if (array_key_exists('geolocation', $eventData)) {
140
                $geolocation = $eventData['geolocation'];
141
                // country
142
                if (array_key_exists('country', $geolocation)) {
143
                    $event->setCountry($geolocation['country']);
144
                    unset($eventData['geolocation']['country']);
145
                }
146
                // city
147
                if (array_key_exists('city', $geolocation)) {
148
                    $event->setCity($geolocation['city']);
149
                    unset($eventData['geolocation']['city']);
150
                }
151
                // region
152
                if (array_key_exists('region', $geolocation)) {
153
                    $event->setRegion($geolocation['region']);
154
                    unset($eventData['geolocation']['region']);
155
                }
156
            }
157
            if (array_key_exists('client-info', $eventData)) {
158
                $clientInfo = $eventData['client-info'];
159
                // clientName
160
                if (array_key_exists('client-name', $clientInfo)) {
161
                    $event->setClientName($clientInfo['client-name']);
162
                    unset($eventData['client-info']['client-name']);
163
                }
164
                // clientOs
165
                if (array_key_exists('client-os', $clientInfo)) {
166
                    $event->setClientOs($clientInfo['client-os']);
167
                    unset($eventData['client-info']['client-os']);
168
                }
169
                // clientType
170
                if (array_key_exists('client-type', $clientInfo)) {
171
                    $event->setClientType($clientInfo['client-type']);
172
                    unset($eventData['client-info']['client-type']);
173
                }
174
                // deviceType
175
                if (array_key_exists('device-type', $clientInfo)) {
176
                    $event->setDeviceType($clientInfo['device-type']);
177
                    unset($eventData['client-info']['device-type']);
178
                }
179
                // userAgent
180
                if (array_key_exists('user-agent', $clientInfo)) {
181
                    $event->setUserAgent($clientInfo['user-agent']);
182
                    unset($eventData['client-info']['user-agent']);
183
                }
184
            }
185
186
            if (array_key_exists('message', $eventData)) {
187
                $message = $eventData['message'];
188
189
                // messageHeaders
190
                if (array_key_exists('headers', $message)) {
191
                    $headers = $message['headers'];
192
                    // messageId
193
                    if (array_key_exists('message-id', $headers)) {
194
                        $trimmedMessageId = trim(trim($headers['message-id']), '<>');
195
                        $event->setMessageId($trimmedMessageId);
196
197
                        // set message domain from message id
198
                        if (null == $event->getDomain()) {
199
                            $event->setDomain(substr($trimmedMessageId, strrpos($trimmedMessageId, '@') + 1));
200
                        }
201
                    }
202
203
                    // sender
204
                    if(array_key_exists('from', $headers)){
205
                        $event->setSender($headers['from']);
206
                    }
207
208
                    $event->setMessageHeaders(json_encode($headers));
209
                    unset($eventData['message']['headers']);
210
                }
211
            }
212
            // campaignName && campaignId
213
            if (array_key_exists('campaigns', $eventData)) {
214
                $event->setCampaignName(print_r($eventData['campaigns'], true));
215
                $event->setCampaignId(print_r($eventData['campaigns'], true));
216
                unset($eventData['campaigns']);
217
            }
218
            // tag
219
            if (array_key_exists('tags', $eventData)) {
220
                $event->setTag(print_r($eventData['tags'], true));
221
                unset($eventData['tags']);
222
            }
223
            // url
224
            if (array_key_exists('url', $eventData)) {
225
                $event->setUrl($eventData['url']);
226
                unset($eventData['url']);
227
            } elseif (array_key_exists('storage', $eventData)) {
228
                $event->setUrl($eventData['storage']['url']);
229
                unset($eventData['storage']['url']);
230
            }
231
232
            // mailingList
233
            if (array_key_exists('recipients', $eventData)) {
234
                $event->setmailingList(print_r($eventData['recipients'], true));
235
                unset($eventData['recipients']);
236
            }
237
238
            $manager = $this->container->get('doctrine.orm.entity_manager');
239
            $manager->persist($event);
240
            $this->getDoctrine()->getRepository(MailgunMessageSummary::class)->createOrUpdateMessageSummary($event);
241
242
            $eventData = $this->removeEmptyArrayElements($eventData);
243
244
            // process the remaining posted values
245
            foreach ($eventData as $key => $value) {
246
                if (0 === strpos($key, 'attachment-')) {
247
                    // create event attachments
248
                    $attachment = new MailgunAttachment($event);
249
                    $attachment->setCounter(substr($key, 11));
0 ignored issues
show
Bug introduced by
substr($key, 11) of type string is incompatible with the type integer expected by parameter $counter of Azine\MailgunWebhooksBun...ttachment::setCounter(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

249
                    $attachment->setCounter(/** @scrutinizer ignore-type */ substr($key, 11));
Loading history...
250
251
                    // get the file
252
                    /* @var $value UploadedFile */
253
                    $attachment->setContent(file_get_contents($value->getPathname()));
254
                    $attachment->setSize($value->getSize());
255
                    $attachment->setType($value->getMimeType());
256
                    $attachment->setName($value->getFilename());
257
258
                    $manager->persist($attachment);
259
                } else {
260
                    // create custom-variables for event
261
                    $customVar = new MailgunCustomVariable($event);
262
                    $customVar->setVariableName($key);
263
                    $customVar->setContent($value);
264
265
                    $manager->persist($customVar);
266
                }
267
            }
268
269
            // save all entities
270
            $manager->flush();
271
272
            // Dispatch an event about the logging of a Webhook-call
273
            $this->get('event_dispatcher')->dispatch(MailgunEvent::CREATE_EVENT, new MailgunWebhookEvent($event));
274
        } catch (\Exception $e) {
275
            $this->container->get('logger')->warning('AzineMailgunWebhooksBundle: creating entities failed: '.$e->getMessage());
0 ignored issues
show
Bug introduced by
The method warning() does not exist on stdClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

275
            $this->container->get('logger')->/** @scrutinizer ignore-call */ warning('AzineMailgunWebhooksBundle: creating entities failed: '.$e->getMessage());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
276
            $this->container->get('logger')->warning($e->getTraceAsString());
277
278
            return new Response(print_r($params, true).'AzineMailgunWebhooksBundle: creating entities failed: '.$e->getMessage(), 500);
279
        }
280
281
        // send response
282
        return new Response('Thanx, for the info.', 200);
283
    }
284
285
    private function createEventOldApi($paramsPre)
286
    {
287
        $params = array_change_key_case($paramsPre, CASE_LOWER);
288
289
        if (sizeof($params) != sizeof($paramsPre)) {
290
            $params['params_contained_duplicate_keys'] = $paramsPre;
291
        }
292
293
        // validate post-data
294
        $key = $this->container->getParameter(AzineMailgunWebhooksExtension::PREFIX.'_'.AzineMailgunWebhooksExtension::API_KEY);
295
        $timestamp = $params['timestamp'];
296
297
        // check if the timestamp is fresh
298
        $now = time();
299
        $tsAge = abs($now - $timestamp);
300
        if ($tsAge > 15) {
301
            return new Response("Signature verification failed. Timestamp too old abs($now - $timestamp)=$tsAge", 401);
302
        }
303
304
        $token = $params['token'];
305
        $expectedSignature = hash_hmac('SHA256', $timestamp.$token, $key);
306
        if ($expectedSignature != $params['signature']) {
307
            return new Response('Signature verification failed.', 401);
308
        }
309
310
        // drop unused variables
311
        if (array_key_exists('x-mailgun-sid', $params)) {
312
            unset($params['x-mailgun-sid']);
313
        }
314
        if (array_key_exists('attachment-count', $params)) {
315
            unset($params['attachment-count']);
316
        }
317
318
        try {
319
            // create event & populate with supplied data
320
            $event = new MailgunEvent();
321
322
            // event
323
            if (array_key_exists('event', $params)) {
324
                $event->setEvent($params['event']);
325
                unset($params['event']);
326
            }
327
328
            // domain
329
            if (array_key_exists('domain', $params)) {
330
                $event->setDomain($params['domain']);
331
                unset($params['domain']);
332
            }
333
            // description
334
            if (array_key_exists('description', $params)) {
335
                $event->setDescription($params['description']);
336
                unset($params['description']);
337
            }
338
            // reason
339
            if (array_key_exists('reason', $params)) {
340
                $event->setReason($params['reason']);
341
                unset($params['reason']);
342
            }
343
            // recipient
344
            if (array_key_exists('recipient', $params)) {
345
                $event->setRecipient($params['recipient']);
346
                unset($params['recipient']);
347
            }
348
            // errorCode
349
            if (array_key_exists('code', $params)) {
350
                $event->setErrorCode($params['code']);
351
                unset($params['code']);
352
            }
353
            // ip
354
            if (array_key_exists('ip', $params)) {
355
                $event->setIp($params['ip']);
356
                unset($params['ip']);
357
            }
358
            // error
359
            if (array_key_exists('error', $params)) {
360
                $event->setDescription($params['error']);
361
                unset($params['error']);
362
            }
363
            // country
364
            if (array_key_exists('country', $params)) {
365
                $event->setCountry($params['country']);
366
                unset($params['country']);
367
            }
368
            // city
369
            if (array_key_exists('city', $params)) {
370
                $event->setCity($params['city']);
371
                unset($params['city']);
372
            }
373
            // region
374
            if (array_key_exists('region', $params)) {
375
                $event->setRegion($params['region']);
376
                unset($params['region']);
377
            }
378
            // campaignId
379
            if (array_key_exists('campaign-id', $params)) {
380
                $event->setCampaignId($params['campaign-id']);
381
                unset($params['campaign-id']);
382
            }
383
            // campaignName	{
384
            if (array_key_exists('campaign-name', $params)) {
385
                $event->setCampaignName($params['campaign-name']);
386
                unset($params['campaign-name']);
387
            }
388
            // clientName
389
            if (array_key_exists('client-name', $params)) {
390
                $event->setClientName($params['client-name']);
391
                unset($params['client-name']);
392
            }
393
            // clientOs
394
            if (array_key_exists('client-os', $params)) {
395
                $event->setClientOs($params['client-os']);
396
                unset($params['client-os']);
397
            }
398
            // clientType
399
            if (array_key_exists('client-type', $params)) {
400
                $event->setClientType($params['client-type']);
401
                unset($params['client-type']);
402
            }
403
            // deviceType
404
            if (array_key_exists('device-type', $params)) {
405
                $event->setDeviceType($params['device-type']);
406
                unset($params['device-type']);
407
            }
408
            // mailingList
409
            if (array_key_exists('mailing-list', $params)) {
410
                $event->setmailingList($params['mailing-list']);
411
                unset($params['mailing-list']);
412
            }
413
            // messageHeaders
414
            if (array_key_exists('message-headers', $params)) {
415
                $headers = json_decode($params['message-headers']);
416
                foreach ($headers as $header) {
417
                    // sender
418
                    if ($header[0] == 'Sender') {
419
                        $event->setSender($header[1]);
420
                    }
421
                }
422
                $event->setMessageHeaders($params['message-headers']);
423
                unset($params['message-headers']);
424
            }
425
            // messageId
426
            if (array_key_exists('message-id', $params)) {
427
                $trimmedMessageId = trim(trim($params['message-id']), '<>');
428
                $event->setMessageId($trimmedMessageId);
429
                unset($params['message-id']);
430
            }
431
            // tag
432
            if (array_key_exists('tag', $params)) {
433
                $event->setTag($params['tag']);
434
                unset($params['tag']);
435
            }
436
            // x-mailgun-tag
437
            if (array_key_exists('x-mailgun-tag', $params)) {
438
                $event->setTag($params['x-mailgun-tag']);
439
                unset($params['x-mailgun-tag']);
440
            }
441
            // userAgent
442
            if (array_key_exists('user-agent', $params)) {
443
                $event->setUserAgent($params['user-agent']);
444
                unset($params['user-agent']);
445
            }
446
            // url
447
            if (array_key_exists('url', $params)) {
448
                $event->setUrl($params['url']);
449
                unset($params['url']);
450
            }
451
            // token
452
            if (array_key_exists('token', $params)) {
453
                $event->setToken($params['token']);
454
                unset($params['token']);
455
            }
456
            // timestamp
457
            if (array_key_exists('timestamp', $params)) {
458
                $event->setTimestamp($params['timestamp']);
459
                unset($params['timestamp']);
460
            }
461
            // signature
462
            if (array_key_exists('signature', $params)) {
463
                $event->setSignature($params['signature']);
464
                unset($params['signature']);
465
            }
466
467
            $manager = $this->container->get('doctrine.orm.entity_manager');
468
            $manager->persist($event);
469
470
            $this->getDoctrine()->getRepository(MailgunMessageSummary::class)->createOrUpdateMessageSummary($event);
471
472
            $params = $this->removeEmptyArrayElements($params);
473
474
            // process the remaining posted values
475
            foreach ($params as $key => $value) {
476
                if (0 === strpos($key, 'attachment-')) {
477
                    // create event attachments
478
                    $attachment = new MailgunAttachment($event);
479
                    $attachment->setCounter(substr($key, 11));
0 ignored issues
show
Bug introduced by
substr($key, 11) of type string is incompatible with the type integer expected by parameter $counter of Azine\MailgunWebhooksBun...ttachment::setCounter(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

479
                    $attachment->setCounter(/** @scrutinizer ignore-type */ substr($key, 11));
Loading history...
480
481
                    // get the file
482
                    /* @var UploadedFile $value */
483
                    $attachment->setContent(file_get_contents($value->getPathname()));
484
                    $attachment->setSize($value->getSize());
485
                    $attachment->setType($value->getMimeType());
486
                    $attachment->setName($value->getFilename());
487
                    $manager->persist($attachment);
488
                } else {
489
                    // create custom-variables for event
490
                    $customVar = new MailgunCustomVariable($event);
491
                    $customVar->setVariableName($key);
492
                    $customVar->setContent($value);
493
                    $manager->persist($customVar);
494
                }
495
            }
496
497
            // save all entities
498
            $manager->flush();
499
500
            // Dispatch an event about the logging of a Webhook-call
501
            $this->get('event_dispatcher')->dispatch(MailgunEvent::CREATE_EVENT, new MailgunWebhookEvent($event));
502
        } catch (\Exception $e) {
503
            $this->container->get('logger')->warning('AzineMailgunWebhooksBundle: creating entities failed: '.$e->getMessage());
504
            $this->container->get('logger')->warning($e->getTraceAsString());
505
506
            return new Response('AzineMailgunWebhooksBundle: creating entities failed: '.$e->getMessage(), 500);
507
        }
508
509
        // send response
510
        return new Response('Thanx, for the info.', 200);
511
    }
512
513
    /**
514
     * @param $haystack
515
     *
516
     * @return array without empty elements (recursively)
517
     */
518
    private function removeEmptyArrayElements($haystack)
519
    {
520
        foreach ($haystack as $key => $value) {
521
            if (is_array($value)) {
522
                $haystack[$key] = $this->removeEmptyArrayElements($haystack[$key]);
523
            }
524
525
            if (empty($haystack[$key])) {
526
                unset($haystack[$key]);
527
            }
528
        }
529
530
        return $haystack;
531
    }
532
}
533