Tools::handleErrors()   A
last analyzed

Complexity

Conditions 6
Paths 9

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 9
nop 2
dl 0
loc 28
rs 9.2222
c 0
b 0
f 0
1
<?php
2
3
namespace Connections\Controller;
4
5
use Auth\Model\User as UserModel;
6
use Connections\Model\Authentication;
7
use Workarea\Model\ConnectionType;
8
use Workarea\Model\ConnectionTypeField;
9
use Workarea\Model\Identifiers;
10
use Workarea\Model\UserConnection;
11
use Workarea\Model\UserConnectionsTable;
12
use Workarea\Model\UserConnectionDetails;
13
use Drone\Db\TableGateway\EntityAdapter;
14
use Drone\Db\TableGateway\TableGateway;
15
use Drone\Dom\Element\Form;
16
use Drone\Mvc\AbstractionController;
17
use Drone\Network\Http;
18
use Drone\Validator\FormValidator;
19
use Utils\Model\Entity as EntityMd;
20
use Drone\Error\Errno;
21
22
class Tools extends AbstractionController
23
{
24
    use \Drone\Error\ErrorTrait;
25
26
    /**
27
     * @var integer
28
     */
29
    private $identity;
0 ignored issues
show
introduced by
The private property $identity is not used, and could be removed.
Loading history...
30
31
    /**
32
     * @var EntityAdapter
33
     */
34
    private $usersEntity;
35
36
    /**
37
     * @var EntityAdapter
38
     */
39
    private $identifiersEntity;
40
41
    /**
42
     * @var EntityAdapter
43
     */
44
    private $connectionTypesEntity;
45
46
    /**
47
     * @var EntityAdapter
48
     */
49
    private $connectionFieldsEntity;
50
51
    /**
52
     * @var EntityAdapter
53
     */
54
    private $userConnectionEntity;
55
56
    /**
57
     * @var EntityAdapter
58
     */
59
    private $userConnectionDetailsEntity;
60
61
    /**
62
     * @return integer
63
     */
64
    private function getIdentity()
0 ignored issues
show
Unused Code introduced by
The method getIdentity() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
65
    {
66
        $config = include 'module/Auth/config/user.config.php';
67
        $method = $config["authentication"]["method"];
68
        $key    = $config["authentication"]["key"];
69
70
        switch ($method)
71
        {
72
            case '_COOKIE':
73
74
                $user = $this->getUsersEntity()->select([
75
                    "USERNAME" => $_COOKIE[$key]
76
                ]);
77
78
                break;
79
80
            case '_SESSION':
81
82
                $user = $this->getUsersEntity()->select([
83
                    "USERNAME" => $_SESSION[$key]
84
                ]);
85
86
                break;
87
        }
88
89
        $user = array_shift($user);
90
91
        return $user->USER_ID;
92
    }
93
94
    /**
95
     * @return UsersEntity
0 ignored issues
show
Bug introduced by
The type Connections\Controller\UsersEntity was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
96
     */
97
    private function getUsersEntity()
98
    {
99
        if (!is_null($this->usersEntity))
100
            return $this->usersEntity;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->usersEntity returns the type Drone\Db\TableGateway\EntityAdapter which is incompatible with the documented return type Connections\Controller\UsersEntity.
Loading history...
101
102
        $this->usersEntity = new EntityAdapter(new TableGateway(new UserModel()));
103
104
        return $this->usersEntity;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->usersEntity returns the type Drone\Db\TableGateway\EntityAdapter which is incompatible with the documented return type Connections\Controller\UsersEntity.
Loading history...
105
    }
106
107
    /**
108
     * @return UsersEntity
109
     */
110
    private function getIdentifiersEntity()
111
    {
112
        if (!is_null($this->identifiersEntity))
113
            return $this->identifiersEntity;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->identifiersEntity returns the type Drone\Db\TableGateway\EntityAdapter which is incompatible with the documented return type Connections\Controller\UsersEntity.
Loading history...
114
115
        $this->identifiersEntity = new EntityAdapter(new TableGateway(new Identifiers()));
116
117
        return $this->identifiersEntity;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->identifiersEntity returns the type Drone\Db\TableGateway\EntityAdapter which is incompatible with the documented return type Connections\Controller\UsersEntity.
Loading history...
118
    }
119
120
    /**
121
     * @return EntityAdapter
122
     */
123
    private function getConnectionTypesEntity()
0 ignored issues
show
Unused Code introduced by
The method getConnectionTypesEntity() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
124
    {
125
        if (!is_null($this->connectionTypesEntity))
126
            return $this->connectionTypesEntity;
127
128
        $this->connectionTypesEntity = new EntityAdapter(new TableGateway(new ConnectionType()));
129
130
        return $this->connectionTypesEntity;
131
    }
132
133
    /**
134
     * @return EntityAdapter
135
     */
136
    private function getConnectionFieldsEntity()
0 ignored issues
show
Unused Code introduced by
The method getConnectionFieldsEntity() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
137
    {
138
        if (!is_null($this->connectionFieldsEntity))
139
            return $this->connectionFieldsEntity;
140
141
        $this->connectionFieldsEntity = new EntityAdapter(new TableGateway(new ConnectionTypeField()));
142
143
        return $this->connectionFieldsEntity;
144
    }
145
146
    /**
147
     * @return EntityAdapter
148
     */
149
    private function getUserConnectionEntity()
150
    {
151
        if (!is_null($this->userConnectionEntity))
152
            return $this->userConnectionEntity;
153
154
        $this->userConnectionEntity = new EntityAdapter(new UserConnectionsTable(new UserConnection()));
155
156
        return $this->userConnectionEntity;
157
    }
158
159
    /**
160
     * @return EntityAdapter
161
     */
162
    private function getUserConnectionDetailsEntity()
163
    {
164
        if (!is_null($this->userConnectionDetailsEntity))
165
            return $this->userConnectionDetailsEntity;
166
167
        $this->userConnectionDetailsEntity = new EntityAdapter(new TableGateway(new UserConnectionDetails()));
168
169
        return $this->userConnectionDetailsEntity;
170
    }
171
172
    /**
173
     * Tests a connection
174
     *
175
     * @return array
176
     */
177
    public function testConnection()
178
    {
179
        clearstatcache();
180
        session_write_close();
181
182
        # data to send
183
        $data = [];
184
185
        # environment settings
186
        $post = $this->getPost();           # catch $_POST
187
        $this->setTerminal(true);           # set terminal
188
189
        # TRY-CATCH-BLOCK
190
        try {
191
192
            # STANDARD VALIDATIONS [check method]
193
            if (!$this->isPost())
194
            {
195
                $http = new Http();
196
                $http->writeStatus($http::HTTP_METHOD_NOT_ALLOWED);
197
198
                die('Error ' . $http::HTTP_METHOD_NOT_ALLOWED .' (' . $http->getStatusText($http::HTTP_METHOD_NOT_ALLOWED) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
199
            }
200
201
            $idenfiers = $this->getIdentifiersEntity()->select([]);
202
            $dbconfig = [];
203
204
            if (array_key_exists('conn_id', $post))
205
            {
206
                # STANDARD VALIDATIONS [check needed arguments]
207
                $needles = ['conn_id'];
208
209
                array_walk($needles, function(&$item) use ($post) {
210
                    if (!array_key_exists($item, $post))
211
                    {
212
                        $http = new Http();
213
                        $http->writeStatus($http::HTTP_BAD_REQUEST);
214
215
                        die('Error ' . $http::HTTP_BAD_REQUEST .' (' . $http->getStatusText($http::HTTP_BAD_REQUEST) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
216
                    }
217
                });
218
219
                $id = $post["conn_id"];
220
221
                $details = $this->getUserConnectionDetailsEntity()->select([
222
                    "USER_CONN_ID" => $id
223
                ]);
224
225
                foreach ($details as $field)
226
                {
227
                    foreach ($idenfiers as $identifier)
228
                    {
229
                        if ($field->CONN_IDENTI_ID == $identifier->CONN_IDENTI_ID)
230
                            $dbconfig[$identifier->CONN_IDENTI_NAME] = $field->FIELD_VALUE;
231
                    }
232
                }
233
            }
234
            else
235
            {
236
                # STANDARD VALIDATIONS [check needed arguments]
237
                $needles = ['type', 'aliasname'];
238
239
                array_walk($needles, function(&$item) use ($post) {
240
                    if (!array_key_exists($item, $post))
241
                    {
242
                        $http = new Http();
243
                        $http->writeStatus($http::HTTP_BAD_REQUEST);
244
245
                        die('Error ' . $http::HTTP_BAD_REQUEST .' (' . $http->getStatusText($http::HTTP_BAD_REQUEST) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
246
                    }
247
                });
248
249
                $components = [
250
                    "attributes" => [
251
                        "type" => [
252
                            "required"  => true,
253
                        ],
254
                        "aliasname" => [
255
                            "required"  => true,
256
                        ]
257
                    ],
258
                ];
259
260
                $options = [
261
                    "type" => [
262
                        "label" => "Value of connection parameter"
263
                    ],
264
                    "aliasname" => [
265
                        "label" => "Type of connection parameter"
266
                    ]
267
                ];
268
269
                $form = new Form($components);
270
                $form->fill($post);
271
272
                $validator = new FormValidator($form, $options);
273
                $validator->validate();
274
275
                $data["validator"] = $validator;
276
277
                # STANDARD VALIDATIONS [check argument constraints]
278
                if (!$validator->isValid())
279
                {
280
                    $data["messages"] = $validator->getMessages();
281
                    throw new \Drone\Exception\Exception("Form validation errors");
282
                }
283
284
                $id = 0;
285
286
                foreach ($post['field'][$post["type"]] as $field_number => $field_value)
287
                {
288
                    foreach ($idenfiers as $identifier)
289
                    {
290
                        if ($field_number == $identifier->CONN_IDENTI_ID)
291
                            $dbconfig[$identifier->CONN_IDENTI_NAME] = $field_value;
292
                    }
293
                }
294
            }
295
296
            try
297
            {
298
                $entity = new EntityMd([]);
299
                $entity->setConnectionIdentifier("CONN" . $id);
300
301
                $driverAdapter = new \Drone\Db\Driver\DriverAdapter($dbconfig, false);
302
                $driverAdapter->getDb()->connect();
303
            }
304
            catch (\Exception $e)
305
            {
306
                # SUCCESS-MESSAGE
307
                $data["process"] = "error";
308
                $data["message"] = $e->getMessage();
309
310
                return $data;
311
            }
312
313
            # SUCCESS-MESSAGE
314
            $data["process"] = "success";
315
        }
316
        catch (\Drone\Exception\Exception $e)
317
        {
318
            # ERROR-MESSAGE
319
            $data["process"] = "warning";
320
            $data["message"] = $e->getMessage();
321
        }
322
        catch (\Exception $e)
323
        {
324
            $file = str_replace('\\', '', __CLASS__);
325
            $storage = new \Drone\Exception\Storage("cache/$file.json");
326
327
            # stores the error code
328
            if (($errorCode = $storage->store($e)) === false)
0 ignored issues
show
introduced by
The condition $errorCode = $storage->store($e) === false is always true.
Loading history...
329
            {
330
                $errors = $storage->getErrors();
331
332
                # if error storing is not possible, handle it (internal app error)
333
                $this->handleErrors($errors, __METHOD__);
334
            }
335
336
            $data["code"]    = $errorCode;
337
            $data["message"] = $e->getMessage();
338
339
            $config = include 'config/application.config.php';
340
            $data["dev_mode"] = $config["environment"]["dev_mode"];
341
342
            # redirect view
343
            $this->setMethod('error');
344
345
            return $data;
346
        }
347
348
        return $data;
349
    }
350
351
    /**
352
     * Puts a worksheet
353
     *
354
     * @return array
355
     */
356
    public function worksheet()
357
    {
358
        # STANDARD VALIDATIONS [check method]
359
        if (!$this->isPost())
360
        {
361
            $http = new Http();
362
            $http->writeStatus($http::HTTP_METHOD_NOT_ALLOWED);
363
364
            die('Error ' . $http::HTTP_METHOD_NOT_ALLOWED .' (' . $http->getStatusText($http::HTTP_METHOD_NOT_ALLOWED) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
365
        }
366
367
        # data to send
368
        $data = [];
369
370
        $this->setTerminal(true);           # set terminal
371
        $post = $this->getPost();           # catch $_POST
372
373
        $data["id"]   = $post["id"];
374
        $data["conn"] = $post["conn"];
375
376
        return $data;
377
    }
378
379
    /**
380
     * Executes a statement
381
     *
382
     * @return array
383
     */
384
    public function execute()
385
    {
386
        clearstatcache();
387
        session_write_close();
388
389
        # data to send
390
        $data = [];
391
392
        # environment settings
393
        $post = $this->getPost();           # catch $_POST
394
        $this->setTerminal(true);           # set terminal
395
396
        # TRY-CATCH-BLOCK
397
        try {
398
399
            # STANDARD VALIDATIONS [check method]
400
            if (!$this->isPost())
401
            {
402
                $http = new Http();
403
                $http->writeStatus($http::HTTP_METHOD_NOT_ALLOWED);
404
405
                die('Error ' . $http::HTTP_METHOD_NOT_ALLOWED .' (' . $http->getStatusText($http::HTTP_METHOD_NOT_ALLOWED) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
406
            }
407
408
            # STANDARD VALIDATIONS [check needed arguments]
409
            $needles = ['conn', 'worksheet'];
410
411
            array_walk($needles, function(&$item) use ($post) {
412
                if (!array_key_exists($item, $post))
413
                {
414
                    $http = new Http();
415
                    $http->writeStatus($http::HTTP_BAD_REQUEST);
416
417
                    die('Error ' . $http::HTTP_BAD_REQUEST .' (' . $http->getStatusText($http::HTTP_BAD_REQUEST) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
418
                }
419
            });
420
421
            $data["worksheet"] = $post["worksheet"];
422
423
            $id = $post["conn"];
424
            $data["conn"] = $id;
425
426
            $connection = $this->getUserConnectionEntity()->select([
427
                "USER_CONN_ID" => $id
428
            ]);
429
430
            if (!count($connection))
431
                throw new \Exception("The Connection does not exists");
432
433
            $connection = array_shift($connection);
434
435
            if ($connection->STATE == 'I')
436
                throw new \Drone\Exception\Exception("This connection was deleted", 300);
437
438
            $details = $this->getUserConnectionDetailsEntity()->select([
439
                "USER_CONN_ID" => $id
440
            ]);
441
442
            $idenfiers = $this->getIdentifiersEntity()->select([]);
443
444
            $dbconfig = [];
445
446
            foreach ($details as $field)
447
            {
448
                foreach ($idenfiers as $identifier)
449
                {
450
                    if ($field->CONN_IDENTI_ID == $identifier->CONN_IDENTI_ID)
451
                        $dbconfig[$identifier->CONN_IDENTI_NAME] = $field->FIELD_VALUE;
452
                }
453
            }
454
455
            /* identifies if sql is base64 encoded */
456
            if (array_key_exists('base64', $post))
457
            {
458
                if ((bool) $post["base64"])
459
                    $post["sql"] = base64_decode($post["sql"]);
460
            }
461
462
            $data["sql"] = base64_encode($post["sql"]);
463
464
            $sql_text = $post["sql"];
465
466
            /*
467
             * SQL parsing
468
             */
469
            $sql_text = trim($sql_text);
470
471
            if (empty($sql_text))
472
                throw new \Drone\Exception\Exception("Empty statement");
473
474
            $pos = strpos($sql_text, ';');
475
476
            if ($pos !== false)
477
            {
478
                $end_stament = strstr($sql_text, ';');
479
480
                if ($end_stament == ';')
481
                    $sql_text = strstr($sql_text, ';', true);
482
            }
483
484
             # clean comments and other characters
485
486
            // (/**/)
487
            $clean_code = preg_replace('/(\s)*\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\//', '', $sql_text);
488
489
            // (--)
490
            $clean_code = preg_replace('/(\s)*--.*\n/', "", $clean_code);
491
492
            # clean other characters starting senteces
493
            $clean_code = preg_replace('/^[\n\t\s]*/', "", $clean_code);
494
495
            # indicates if SQL is a selection statement
496
            $isSelectStm = $data["selectStm"] = (preg_match('/^SELECT/i', $clean_code));
497
498
            # indicates if SQL is a show statement
499
            $isShowStm   = $data["showStm"]   = (preg_match('/^SHOW/i', $clean_code));
500
501
            # detect selection
502
            if ($isSelectStm || $isShowStm)
503
            {
504
                $step = 10;
505
506
                $row_start = 0;
507
                $row_end   = $step;
508
509
                if (array_key_exists('row_start', $post) && array_key_exists('row_end', $post))
510
                {
511
                    $components = [
512
                        "attributes" => [
513
                            "row_start" => [
514
                                "required" => true,
515
                                "type"     => "number",
516
                                "min"      => 0
517
                            ],
518
                            "row_end" => [
519
                                "required" => true,
520
                                "type"     => "number",
521
                                "min"      => 0
522
                            ],
523
                        ],
524
                    ];
525
526
                    $options = [
527
                        "row_start" => [
528
                            "label" => "Start row",
529
                        ],
530
                        "row_end" => [
531
                            "label" => "End row",
532
                        ],
533
                    ];
534
535
                    $form = new Form($components);
536
                    $form->fill($post);
537
538
                    $validator = new FormValidator($form, $options);
539
                    $validator->validate();
540
541
                    # STANDARD VALIDATIONS [check argument constraints]
542
                    if (!$validator->isValid())
543
                    {
544
                        $http = new Http();
545
                        $http->writeStatus($http::HTTP_BAD_REQUEST);
546
547
                        die('Error ' . $http::HTTP_BAD_REQUEST .' (' . $http->getStatusText($http::HTTP_BAD_REQUEST) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
548
                    }
549
550
                    $row_start = $post["row_start"] + $step;
551
                    $row_end   = $post["row_end"] + $step;
552
                }
553
554
                switch (strtolower($dbconfig["driver"]))
555
                {
556
                    case 'mysqli':
557
558
                        # show statement cannot be a subquery
559
                        if (!$isShowStm)
560
                            $sql_text = "SELECT (@ROW_NUM:=@ROW_NUM + 1) AS ROW_NUM, V.* FROM (
561
                                            " . $sql_text . "
562
                                        ) V LIMIT $row_start, $step";
563
                        break;
564
565
                    case 'oci8':
566
567
                        $start = $row_start + 1;
568
569
                        $sql_text = "SELECT * FROM (
570
                                        SELECT ROWNUM ROW_NUM, V.* FROM (" . $sql_text . ") V
571
                                    ) VV WHERE VV.ROW_NUM BETWEEN $start AND $row_end";
572
                        break;
573
574
                    case 'sqlsrv':
575
576
                        $start = $row_start + 1;
577
578
                        $sql_text = "SELECT VV.*
579
                                    FROM (
580
                                        SELECT ROW_NUMBER() OVER(ORDER BY (
581
                                            SELECT TOP 1 NAME
582
                                            FROM SYS.DM_EXEC_DESCRIBE_FIRST_RESULT_SET('$sql_text', NULL, 0))
583
                                        ) AS ROW_NUM, V.*
584
                                        FROM ( $sql_text ) V
585
                                    ) VV
586
                                    WHERE VV.ROW_NUM BETWEEN $start AND $row_end";
587
                        break;
588
589
                    default:
590
                        # code...
591
                        break;
592
                }
593
594
                $data["row_start"] = $row_start;
595
                $data["row_end"]   = $row_end;
596
            }
597
598
            try {
599
600
                $connError = false;
601
602
                $entity = new EntityMd([]);
603
                $entity->setConnectionIdentifier("CONN" . $id);
604
605
                $driverAdapter = new \Drone\Db\Driver\DriverAdapter($dbconfig, false);
606
607
                # start time to compute execution
608
                $startTime = microtime(true);
609
610
                $driverAdapter->getDb()->connect();
611
612
                $auth = $driverAdapter;
613
614
                $data["results"] = $auth->getDb()->execute($sql_text);
615
            }
616
            # encapsulate real connection error!
617
            catch (\Drone\Db\Driver\Exception\ConnectionException $e)
618
            {
619
                $connError = true;
620
621
                $file = str_replace('\\', '', __CLASS__);
622
                $storage = new \Drone\Exception\Storage("cache/$file.json");
623
624
                if (($errorCode = $storage->store($e)) === false)
0 ignored issues
show
introduced by
The condition $errorCode = $storage->store($e) === false is always true.
Loading history...
625
                {
626
                    $errors = $storage->getErrors();
627
                    $this->handleErrors($errors, __METHOD__);
628
                }
629
630
                $data["code"]    = $errorCode;
631
                $data["message"] = "Could not connect to database!";
632
633
                # to identify development mode
634
                $config = include 'config/application.config.php';
635
                $data["dev_mode"] = $config["environment"]["dev_mode"];
636
637
                # redirect view
638
                $this->setMethod('error');
639
            }
640
            catch (\Exception $e)
641
            {
642
                # SUCCESS-MESSAGE
643
                $data["process"] = "error";
644
                $data["message"] = $e->getMessage();
645
646
                return $data;
647
            }
648
649
            # end time to compute execution
650
            $endTime = microtime(true);
651
            $elapsed_time = $endTime - $startTime;
652
653
            $data["time"] = round($elapsed_time, 4);
654
655
            if (!$connError)
656
            {
657
                $data["num_rows"]      = $auth->getDb()->getNumRows();
658
                $data["num_fields"]    = $auth->getDb()->getNumFields();
659
                $data["rows_affected"] = $auth->getDb()->getRowsAffected();
660
661
                # cumulative results
662
                if ($isSelectStm && array_key_exists('num_rows', $post) && array_key_exists('time', $post))
663
                {
664
                    $data["num_rows"] += $post["num_rows"];
665
                    $data["time"]     += $post["time"];
666
                }
667
668
                $data["data"] = [];
669
670
                # redirect view
671
                if ($isSelectStm || $isShowStm)
672
                {
673
                    $rows = $auth->getDb()->getArrayResult();
674
675
                    $k = 0;
676
677
                    # columns with errors in a select statement
678
                    $column_errors = [];
679
680
                    # data parsing
681
                    foreach ($rows as $key => $row)
682
                    {
683
                        $k++;
684
685
                        $data["data"][$key] = [];
686
687
                        if ($isShowStm)
688
                        {
689
                            $data["data"][$key]["ROW_NUM"] = $k;
690
                            $data["data"][$key][0] = $k;
691
                        }
692
693
                        foreach ($row as $column => $value)
694
                        {
695
                            if ($isShowStm)
696
                                $column++;
697
698
                            if (gettype($value) == 'object')
699
                            {
700
                                if  (get_class($value) == 'OCI-Lob')
701
                                {
702
                                    if (($val = @$value->load()) === false)
703
                                    {
704
                                        $val = null;   # only for default, this value is not used
705
                                        $column_errors[] = $column;
706
                                    }
707
708
                                    $data["data"][$key][$column] = $val;
709
                                }
710
                                else
711
                                    $data["data"][$key][$column] = $value;
712
                            }
713
                            else {
714
                                $data["data"][$key][$column] = $value;
715
                            }
716
                        }
717
                    }
718
719
                    $data["column_errors"] = $column_errors;
720
721
                    if ($row_start > 1)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $row_start does not seem to be defined for all execution paths leading up to this point.
Loading history...
722
                        $this->setMethod('nextResults');
723
                }
724
725
                if (array_key_exists('id', $post))
726
                    $data["id"] = $post["id"];
727
728
                # SUCCESS-MESSAGE
729
                $data["process"] = "success";
730
            }
731
        }
732
        catch (\Drone\Exception\Exception $e)
733
        {
734
            # ERROR-MESSAGE
735
            $data["process"] = "warning";
736
            $data["message"] = $e->getMessage();
737
        }
738
        catch (\Exception $e)
739
        {
740
            $file = str_replace('\\', '', __CLASS__);
741
            $storage = new \Drone\Exception\Storage("cache/$file.json");
742
743
            # stores the error code
744
            if (($errorCode = $storage->store($e)) === false)
0 ignored issues
show
introduced by
The condition $errorCode = $storage->store($e) === false is always true.
Loading history...
745
            {
746
                $errors = $storage->getErrors();
747
748
                # if error storing is not possible, handle it (internal app error)
749
                $this->handleErrors($errors, __METHOD__);
750
            }
751
752
            $data["code"]    = $errorCode;
753
            $data["message"] = $e->getMessage();
754
755
            $config = include 'config/application.config.php';
756
            $data["dev_mode"] = $config["environment"]["dev_mode"];
757
758
            # redirect view
759
            $this->setMethod('error');
760
761
            return $data;
762
        }
763
764
        return $data;
765
    }
766
767
    /**
768
     * Exports a statement
769
     *
770
     * @return array
771
     */
772
    public function export()
773
    {
774
        clearstatcache();
775
        session_write_close();
776
777
        # data to send
778
        $data = [];
779
780
        # environment settings
781
        $post = $this->getPost();           # catch $_POST
782
        $this->setTerminal(true);           # set terminal
783
784
        # TRY-CATCH-BLOCK
785
        try {
786
787
            # STANDARD VALIDATIONS [check method]
788
            if (!$this->isPost())
789
            {
790
                $http = new Http();
791
                $http->writeStatus($http::HTTP_METHOD_NOT_ALLOWED);
792
793
                die('Error ' . $http::HTTP_METHOD_NOT_ALLOWED .' (' . $http->getStatusText($http::HTTP_METHOD_NOT_ALLOWED) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
794
            }
795
796
            # STANDARD VALIDATIONS [check needed arguments]
797
            $needles = ['conn', 'sql', 'type', 'filename'];
798
799
            array_walk($needles, function(&$item) use ($post) {
800
                if (!array_key_exists($item, $post))
801
                {
802
                    $http = new Http();
803
                    $http->writeStatus($http::HTTP_BAD_REQUEST);
804
805
                    die('Error ' . $http::HTTP_BAD_REQUEST .' (' . $http->getStatusText($http::HTTP_BAD_REQUEST) . ')!!');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
806
                }
807
            });
808
809
            $components = [
810
                "attributes" => [
811
                    "conn" => [
812
                        "required"  => true,
813
                        "type"      => "number"
814
                    ],
815
                    "sql" => [
816
                        "required"  => true,
817
                        "type"      => "text"
818
                    ],
819
                    "type" => [
820
                        "required"  => true,
821
                        "type"      => "text"
822
                    ],
823
                    "filename" => [
824
                        "required"  => true,
825
                        "type"      => "text"
826
                    ]
827
                ],
828
            ];
829
830
            $options = [
831
                "conn" => [
832
                    "label" => "Connection",
833
                ],
834
                "sql" => [
835
                    "label" => "SQL",
836
                    "validators" => [
837
                        "Regex" => ["pattern" => '/^[a-zA-Z0-9\+\/]+$/']
838
                    ]
839
                ],
840
                "type" => [
841
                    "label" => "Type",
842
                    "validators" => [
843
                        "InArray" => ["haystack" => ['excel', 'csv']]
844
                    ]
845
                ],
846
                "filename" => [
847
                    "label" => "Filename"
848
                ]
849
            ];
850
851
            $form = new Form($components);
852
            $form->fill($post);
853
854
            $validator = new FormValidator($form, $options);
855
            $validator->validate();
856
857
            $data["validator"] = $validator;
858
859
            # form validation
860
            if (!$validator->isValid())
861
            {
862
                $data["messages"] = $validator->getMessages();
863
                throw new \Drone\Exception\Exception("Form validation errors", 300);
864
            }
865
866
            $id = $post["conn"];
867
868
            $connection = $this->getUserConnectionEntity()->select([
869
                "USER_CONN_ID" => $id
870
            ]);
871
872
            if (!count($connection))
873
                throw new \Exception("The Connection does not exists");
874
875
            $connection = array_shift($connection);
876
877
            if ($connection->STATE == 'I')
878
                throw new \Drone\Exception\Exception("This connection was deleted", 300);
879
880
            $details = $this->getUserConnectionDetailsEntity()->select([
881
                "USER_CONN_ID" => $id
882
            ]);
883
884
            $idenfiers = $this->getIdentifiersEntity()->select([]);
885
886
            $dbconfig = [];
887
888
            foreach ($details as $field)
889
            {
890
                foreach ($idenfiers as $identifier)
891
                {
892
                    if ($field->CONN_IDENTI_ID == $identifier->CONN_IDENTI_ID)
893
                        $dbconfig[$identifier->CONN_IDENTI_NAME] = $field->FIELD_VALUE;
894
                }
895
            }
896
897
            /* sql post value muest be ever base64 encoded */
898
            $post["sql"] = base64_decode($post["sql"]);
899
            $data["sql"] = $post["sql"];
900
901
            $sql_text = $post["sql"];
902
903
            /*
904
             * SQL parsing
905
             */
906
            $sql_text = trim($sql_text);
907
908
            if (empty($sql_text))
909
                throw new \Drone\Exception\Exception("Empty statement");
910
911
            $pos = strpos($sql_text, ';');
912
913
            if ($pos !== false)
914
            {
915
                $end_stament = strstr($sql_text, ';');
916
917
                if ($end_stament == ';')
918
                    $sql_text = strstr($sql_text, ';', true);
919
            }
920
921
             # clean comments and other characters
922
923
            // (/**/)
924
            $clean_code = preg_replace('/(\s)*\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\//', '', $sql_text);
925
926
            // (--)
927
            $clean_code = preg_replace('/(\s)*--.*\n/', "", $clean_code);
928
929
            # clean other characters starting senteces
930
            $clean_code = preg_replace('/^[\n\t\s]*/', "", $clean_code);
931
932
            # indicates if SQL is a selection statement
933
            $isSelectStm = $data["selectStm"] = (preg_match('/^SELECT/i', $clean_code));
934
935
            # indicates if SQL is a show statement
936
            $isShowStm   = $data["showStm"]   = (preg_match('/^SHOW/i', $clean_code));
937
938
            # detect selection
939
            if (!$isSelectStm && !$isShowStm)
940
                throw new \Exception("You can't export a non-selection statement!");
941
942
            try {
943
944
                $connError = false;
945
946
                $entity = new EntityMd([]);
947
                $entity->setConnectionIdentifier("CONN" . $id);
948
949
                $driverAdapter = new \Drone\Db\Driver\DriverAdapter($dbconfig, false);
950
951
                # start time to compute execution
952
                $startTime = microtime(true);
953
954
                $driverAdapter->getDb()->connect();
955
956
                $auth = $driverAdapter;
957
958
                $data["results"] = $auth->getDb()->execute($sql_text);
959
            }
960
            # encapsulate real connection error!
961
            catch (\Drone\Db\Driver\Exception\ConnectionException $e)
962
            {
963
                $connError = true;
964
965
                $file = str_replace('\\', '', __CLASS__);
966
                $storage = new \Drone\Exception\Storage("cache/$file.json");
967
968
                # stores the error code
969
                if (($errorCode = $storage->store($e)) === false)
0 ignored issues
show
introduced by
The condition $errorCode = $storage->store($e) === false is always true.
Loading history...
970
                {
971
                    $errors = $storage->getErrors();
972
973
                    # if error storing is not possible, handle it (internal app error)
974
                    $this->handleErrors($errors, __METHOD__);
975
                }
976
977
                $data["code"]    = $errorCode;
978
                $data["message"] = "Could not connect to database";
979
980
                # to identify development mode
981
                $config = include 'config/application.config.php';
982
                $data["dev_mode"] = $config["environment"]["dev_mode"];
983
984
                # redirect view
985
                $this->setMethod('error');
986
            }
987
            catch (\Exception $e)
988
            {
989
                # SUCCESS-MESSAGE
990
                $data["process"] = "error";
991
                $data["message"] = $e->getMessage();
992
993
                return $data;
994
            }
995
996
            # end time to compute execution
997
            $endTime = microtime(true);
998
            $elapsed_time = $endTime - $startTime;
999
1000
            $data["time"] = round($elapsed_time, 4);
1001
1002
            if (!$connError)
1003
            {
1004
                $data["num_rows"]      = $auth->getDb()->getNumRows();
1005
                $data["num_fields"]    = $auth->getDb()->getNumFields();
1006
                $data["rows_affected"] = $auth->getDb()->getRowsAffected();
1007
1008
                $rows = $auth->getDb()->getArrayResult();
1009
1010
                # columns with errors in a select statement
1011
                $column_errors = [];
1012
1013
                switch ($post["type"])
1014
                {
1015
                    case 'excel':
1016
                        $ext = '.xls';
1017
                        break;
1018
                    case 'csv':
1019
                        $ext = '.csv';
1020
                        break;
1021
                    default:
1022
                        $ext = '.txt';
1023
                        break;
1024
                }
1025
1026
                $filename = $post["filename"] . $ext;
1027
1028
                $file_hd = @fopen("cache/" . $filename, "w+");
1029
1030
                if (!$file_hd)
0 ignored issues
show
introduced by
$file_hd is of type false|resource, thus it always evaluated to false.
Loading history...
1031
                {
1032
                    $this->error(Errno::FILE_PERMISSION_DENIED, "cache/" . $filename);
1033
                    throw new \Exception("The file could not be created!");
1034
                }
1035
1036
                $contents = "";
1037
1038
                $data["data"] = [];
1039
1040
                switch ($post["type"])
1041
                {
1042
                    case 'excel':
1043
1044
                        $table = "<html xmlns:v='urn:schemas-microsoft-com:vml' \r\n\txmlns:o='urn:schemas-microsoft-com:office:office'\r\n";
1045
                        $table .= "\txmlns:x='urn:schemas-microsoft-com:office:excel'\r\n";
1046
                        $table .= "\txmlns='http://www.w3.org/TR/REC-html40'>\r\n";
1047
1048
                        $table .= "<head>\r\n";
1049
                        $table .= "\t<meta name='Excel Workbook Frameset'><meta http-equiv='Content-Type' content='text/html; charset='utf-8'>\r\n";
1050
                        $table .= "</head>\r\n\r\n";
1051
1052
                        $table .= "<body>\r\n<table border=1>\r\n";
1053
1054
                        $column_names = [];
1055
1056
                        foreach ($rows[0] as $column_name => $row)
1057
                        {
1058
                            if (!is_numeric($column_name))
1059
                                $column_names[] = $column_name;
1060
                        }
1061
1062
                        $table .= "\t<thead>\r\n\t\t<tr>\r\n";
1063
1064
                        foreach ($column_names as $column_name)
1065
                        {
1066
                            $table .= "\t\t\t<th>$column_name</th>\r\n";
1067
                        }
1068
1069
                        $table .= "\t\t</tr>\r\n\t</thead>\r\n\t<tbody>";
1070
1071
                        # data parsing
1072
                        foreach ($rows as $key => $row)
1073
                        {
1074
                            $data["data"][$key] = [];
1075
1076
                            foreach ($row as $column => $value)
1077
                            {
1078
                                if ($isShowStm)
1079
                                    $column++;
1080
1081
                                if (gettype($value) == 'object')
1082
                                {
1083
                                    if  (get_class($value) == 'OCI-Lob')
1084
                                    {
1085
                                        if (($val = @$value->load()) === false)
1086
                                        {
1087
                                            $val = null;   # only for default, this value is not used
1088
                                            $column_errors[] = $column;
1089
                                        }
1090
1091
                                        $data["data"][$key][$column] = $val;
1092
                                    }
1093
                                    else
1094
                                        $data["data"][$key][$column] = $value;
1095
                                }
1096
                                else {
1097
                                    $data["data"][$key][$column] = $value;
1098
                                }
1099
                            }
1100
1101
                        }
1102
1103
                        foreach ($data["data"] as $row)
1104
                        {
1105
                            $table .= "\t\t<tr>\r\n";
1106
1107
                            foreach ($column_names as $column_name)
1108
                            {
1109
                                $table .= "\t\t\t<td>". $row[$column_name] ."</td>\r\n";
1110
                            }
1111
1112
                            $table .= "\t\t</tr>\r\n";
1113
                        }
1114
1115
                        $table .= "\t</tbody>\r\n</table>\r\n</body>\r\n</html>";
1116
                        $contents = $table;
1117
1118
                        break;
1119
1120
                    case 'csv':
1121
1122
                        $text = "";
1123
1124
                        $column_names = [];
1125
1126
                        foreach ($rows[0] as $column_name => $row)
1127
                        {
1128
                            if (!is_numeric($column_name))
1129
                                $column_names[] = $column_name;
1130
                        }
1131
1132
                        foreach ($column_names as $column_name)
1133
                        {
1134
                            $text .= "$column_name;";
1135
                        }
1136
1137
                        $text .= "\r\n";
1138
1139
                        # data parsing
1140
                        foreach ($rows as $key => $row)
1141
                        {
1142
                            $data["data"][$key] = [];
1143
1144
                            foreach ($row as $column => $value)
1145
                            {
1146
                                if ($isShowStm)
1147
                                    $column++;
1148
1149
                                if (gettype($value) == 'object')
1150
                                {
1151
                                    if  (get_class($value) == 'OCI-Lob')
1152
                                    {
1153
                                        if (($val = @$value->load()) === false)
1154
                                        {
1155
                                            $val = null;   # only for default, this value is not used
1156
                                            $column_errors[] = $column;
1157
                                        }
1158
1159
                                        $data["data"][$key][$column] = $val;
1160
                                    }
1161
                                    else
1162
                                        $data["data"][$key][$column] = $value;
1163
                                }
1164
                                else {
1165
                                    $data["data"][$key][$column] = $value;
1166
                                }
1167
                            }
1168
                        }
1169
1170
                        foreach ($data["data"] as $row)
1171
                        {
1172
                            foreach ($column_names as $column_name)
1173
                            {
1174
                                $text .= $row[$column_name] . ";";
1175
                            }
1176
1177
                            $text .= "\r\n";
1178
                        }
1179
1180
                        $contents = $text;
1181
1182
                        break;
1183
1184
                    default:
1185
                        # code...
1186
                        break;
1187
                }
1188
1189
                if (!@fwrite($file_hd, $contents))
1190
                {
1191
                    $this->error(Errno::FILE_PERMISSION_DENIED, "cache/" . $filename);
1192
                    throw new \Exception("The file could not be generated!");
1193
                }
1194
1195
                @fclose($file_hd);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1195
                /** @scrutinizer ignore-unhandled */ @fclose($file_hd);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1196
1197
                $data["column_errors"] = $column_errors;
1198
1199
                $data["filename"] = $filename;
1200
1201
                if (array_key_exists('id', $post))
1202
                    $data["id"] = $post["id"];
1203
1204
                # SUCCESS-MESSAGE
1205
                $data["process"] = "success";
1206
            }
1207
        }
1208
        catch (\Drone\Exception\Exception $e)
1209
        {
1210
            # ERROR-MESSAGE
1211
            $data["process"] = "warning";
1212
            $data["message"] = $e->getMessage();
1213
        }
1214
        catch (\Exception $e)
1215
        {
1216
            $file = str_replace('\\', '', __CLASS__);
1217
            $storage = new \Drone\Exception\Storage("cache/$file.json");
1218
1219
            # stores the error code
1220
            if (($errorCode = $storage->store($e)) === false)
0 ignored issues
show
introduced by
The condition $errorCode = $storage->store($e) === false is always true.
Loading history...
1221
            {
1222
                $errors = $storage->getErrors();
1223
1224
                # if error storing is not possible, handle it (internal app error)
1225
                $this->handleErrors($errors, __METHOD__);
1226
            }
1227
1228
            # errors retrived by the use of ErrorTrait
1229
            if (count($this->getErrors()))
1230
                $this->handleErrors($this->getErrors(), __METHOD__);
1231
1232
            $data["code"]    = $errorCode;
1233
            $data["message"] = $e->getMessage();
1234
1235
            $config = include 'config/application.config.php';
1236
            $data["dev_mode"] = $config["environment"]["dev_mode"];
1237
1238
            # redirect view
1239
            $this->setMethod('error');
1240
1241
            return $data;
1242
        }
1243
1244
        return $data;
1245
    }
1246
1247
    private function handleErrors(Array $errors, $method)
1248
    {
1249
        if (count($errors))
1250
        {
1251
            $errorInformation = "";
1252
1253
            foreach ($errors as $errno => $error)
1254
            {
1255
                $errorInformation .=
1256
                    "<strong style='color: #a94442'>".
1257
                        $method
1258
                            . "</strong>: <span style='color: #e24f4c'>{$error}</span> \n<br />";
1259
            }
1260
1261
            $hd = @fopen('cache/errors.txt', "a");
1262
1263
            if (!$hd || !@fwrite($hd, $errorInformation))
0 ignored issues
show
introduced by
$hd is of type false|resource, thus it always evaluated to false.
Loading history...
1264
            {
1265
                # error storing are not mandatory!
1266
            }
1267
            else
1268
                @fclose($hd);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1268
                /** @scrutinizer ignore-unhandled */ @fclose($hd);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1269
1270
            $config = include 'config/application.config.php';
1271
            $dev = $config["environment"]["dev_mode"];
1272
1273
            if ($dev)
1274
                echo $errorInformation;
1275
        }
1276
    }
1277
}