Completed
Push — typo3v9 ( 4169e7...0dcd72 )
by Tomas Norre
05:25
created

isRequestHashMatchingQueueRecord()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
namespace AOE\Crawler\Middleware;
4
5
/*
6
 * This file is part of the TYPO3 Crawler Extension
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
19
use AOE\Crawler\Domain\Repository\QueueRepository;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use Psr\Http\Server\MiddlewareInterface;
23
use Psr\Http\Server\RequestHandlerInterface;
24
use TYPO3\CMS\Core\Context\Context;
25
use TYPO3\CMS\Core\Http\Response;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
use TYPO3\CMS\Extbase\Object\ObjectManager;
28
use TYPO3\CMS\Frontend\Controller\ErrorController;
29
30
/**
31
 * Evaluates HTTP headers and checks if Crawler should register itself.
32
 * Needs to be run after TSFE initialization AND Frontend User Authentication.
33
 *
34
 * Once done, the queue is fetched, and then the frontend request runs through.
35
 *
36
 * Finally, at the very end, if the crawler is still running, output the data and replace the response.
37
 */
38
class CrawlerInitialization implements MiddlewareInterface
39
{
40
    /**
41
     * @var string
42
     */
43
    protected $headerName = 'X-T3CRAWLER';
44
45
    /**
46
     * @var Context
47
     */
48
    protected $context;
49
50
    /**
51
     * @var QueueRepository
52
     */
53
    protected $queueRepository;
54
55
    /**
56
     * @param Context|null $context
57
     */
58
    public function __construct(Context $context = null)
59
    {
60
        $this->context = $context ?? GeneralUtility::makeInstance(Context::class);
61
        $this->queueRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(QueueRepository::class);
62
    }
63
64
    /**
65
     * @param ServerRequestInterface $request
66
     * @param RequestHandlerInterface $handler
67
     * @return ResponseInterface
68
     * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
69
     * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
70
     */
71
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
72
    {
73
        $crawlerInformation = $request->getHeaderLine($this->headerName) ?? null;
74
        if (empty($crawlerInformation)) {
75
            return $handler->handle($request);
76
        }
77
78
        // Authenticate crawler request:
79
        //@todo: ask service to exclude current call for special reasons: for example no relevance because the language version is not affected
80
        [$queueId, $hash] = explode(':', $crawlerInformation);
0 ignored issues
show
Bug introduced by
The variable $queueId does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $hash does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
81
        $queueRec = $this->queueRepository->findByQueueId($queueId);
82
83
        // If a crawler record was found and hash was matching, set it up
84
        if (!$this->isRequestHashMatchingQueueRecord($queueRec, $hash)) {
85
            return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, 'No crawler entry found');
86
        }
87
88
        $queueParameters = unserialize($queueRec['parameters']);
89
        $GLOBALS['TSFE']->applicationData['tx_crawler']['running'] = true;
90
        $GLOBALS['TSFE']->applicationData['tx_crawler']['parameters'] = $queueParameters;
91
        $GLOBALS['TSFE']->applicationData['tx_crawler']['log'] = [];
92
        $request = $request->withAttribute('tx_crawler', $queueParameters);
93
94
        // Now ensure to set the proper user groups
95
        $grList = $queueParameters['feUserGroupList'];
96
        if ($grList) {
97
            if (!is_array($GLOBALS['TSFE']->fe_user->user)) {
98
                $GLOBALS['TSFE']->fe_user->user = [];
99
            }
100
            $GLOBALS['TSFE']->fe_user->user['usergroup'] = $grList;
101
            $GLOBALS['TSFE']->applicationData['tx_crawler']['log'][] = 'User Groups: ' . $grList;
102
            // @todo: set the frontend user aspect again.
103
        }
104
105
        // Execute the frontend request as is
106
        $handler->handle($request);
107
108
        $GLOBALS['TSFE']->applicationData['tx_crawler']['vars'] = [
109
            'id' => $GLOBALS['TSFE']->id,
110
            'gr_list' => implode(',', $this->context->getAspect('frontend.user')->getGroupIds()),
111
            'no_cache' => $GLOBALS['TSFE']->no_cache,
112
        ];
113
114
        $this->runPollSuccessHooks();
115
116
        // Output log data for crawler (serialized content):
117
        $content = serialize($GLOBALS['TSFE']->applicationData['tx_crawler']);
118
        $response = new Response();
119
        $response->getBody()->write($content);
120
        return $response;
121
    }
122
123
    protected function isRequestHashMatchingQueueRecord(?array $queueRec, string $hash): bool
124
    {
125
        return is_array($queueRec) && $hash === md5($queueRec['qid'] . '|' . $queueRec['set_id'] . '|' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
126
    }
127
128
    /**
129
     * Required because some extensions (staticpub) might never be requested to run due to some Core side effects
130
     * and since this is considered as error the crawler should handle it properly
131
     */
132
    protected function runPollSuccessHooks()
133
    {
134
        if (!is_array($GLOBALS['TSFE']->applicationData['tx_crawler']['content']['parameters']['procInstructions'])) {
135
            return;
136
        }
137
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['crawler']['pollSuccess'] ?? [] as $pollable) {
138
            if (in_array($pollable, $GLOBALS['TSFE']->applicationData['tx_crawler']['content']['parameters']['procInstructions'])) {
139
                if (empty($GLOBALS['TSFE']->applicationData['tx_crawler']['success'][$pollable])) {
140
                    $GLOBALS['TSFE']->applicationData['tx_crawler']['errorlog'][] = 'Error: Pollable extension (' . $pollable . ') did not complete successfully.';
141
                }
142
            }
143
        }
144
    }
145
146
}
147