Completed
Push — fm-support ( 2f8546...624fa6 )
by Konstantinos
04:31
created

LeagueOverseerHookController   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 4
Bugs 1 Features 3
Metric Value
wmc 37
c 4
b 1
f 3
lcom 1
cbo 13
dl 0
loc 277
rs 8.6

8 Methods

Rating   Name   Duplication   Size   Complexity  
B setUp() 0 35 6
B queryAction() 0 17 7
B teamNameAction() 0 24 4
B teamNameDumpAction() 0 40 4
C matchReportAction() 0 77 8
A bzidsToIdArray() 0 10 2
B getTeam() 0 19 5
A getLogChannel() 0 4 1
1
<?php
2
3
use Monolog\Logger;
4
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
5
use Symfony\Component\HttpFoundation\JsonResponse;
6
use Symfony\Component\HttpFoundation\Request;
7
8
class LeagueOverseerHookController extends PlainTextController
9
{
10
    /**
11
     * The API version of the server performing the request
12
     * @var int
13
     */
14
    private $version;
15
16
    /**
17
     * The parameter bag representing the $_GET or $_POST array
18
     * @var Symfony\Component\HttpFoundation\ParameterBag
19
     */
20
    private $params;
21
22
    /**
23
     * {@inheritdoc}
24
     */
25
    public function setUp()
26
    {
27
        $request = $this->getRequest();
28
29
        // To prevent abuse of the automated system, we need to make sure that
30
        // the IP making the request is one of the IPs we allowed in the config
31
        $allowedIPs = array_map('trim', $this->container->getParameter('bzion.api.allowed_ips'));
32
        $clientIP   = $request->getClientIp();
33
34
        if (!$this->isDebug() && // Don't care about IPs if we're in debug mode
35
           !in_array($clientIP, $allowedIPs)) {
36
            // If server making the request isn't an official server, then log the unauthorized attempt and kill the script
37
38
            $this->getLogger()->addNotice("Unauthorized access attempt from $clientIP");
39
            throw new ForbiddenException("Error: 403 - Forbidden");
40
        }
41
42
        // We will be looking at either $_POST or $_GET depending on the status, production or development
43
        $this->params = $request->request; // $_POST
44
45
        if (!$this->params->has('query')) {
46
            // There seems to be nothing in $_POST. If we are in debug mode
47
            // however, we might have a debug request with data in $_GET
48
            if ($this->isDebug() && $request->query->has('query')) {
49
                $this->params = $request->query; // $_GET
50
            } else {
51
                throw new BadRequestException();
52
            }
53
        }
54
55
        // After the first major rewrite of the league overseer plugin, the
56
        // API was introduced in order to provide backwards compatibility for
57
        // servers that have not updated to the latest version of the plugin.
58
        $this->version = $this->params->get('apiVersion', 0);
59
    }
60
61
    /**
62
     * @ApiDoc(
63
     *  description="Query the LeagueOverseer API",
64
     *  parameters={
65
     *      {"name"="query", "dataType"="string", "required"=true, "description"="query type"},
66
     *      {"name"="apiVersion", "dataType"="integer", "required"=false, "description"="LeagueOverseer API version"}
67
     *  }
68
     * )
69
     * @todo Test/improve/revoke support for API version 0
70
     */
71
    public function queryAction()
72
    {
73
        $matchReportQuery = $this->version == 1 ? 'reportMatch' : 'matchReport';
74
        $teamNameQuery = $this->version == 1 ? 'teamNameQuery' : 'teamName';
75
        $teamNameDumpQuery = $this->version == 1 ? 'teamDump' : 'teamInfoDump';
76
77
        switch ($this->params->get('query')) {
78
            case $matchReportQuery:
79
                return $this->forward('matchReport');
80
            case $teamNameQuery:
81
                return $this->forward('teamName');
82
            case $teamNameDumpQuery:
83
                return $this->forward('teamNameDump');
84
            default:
85
                throw new BadRequestException();
86
            }
87
    }
88
89
    public function teamNameAction()
90
    {
91
        if ($this->version < 1) {
92
            throw new BadRequestException();
93
        }
94
95
        $param = $this->version == 1 ? 'teamPlayers' : 'bzid';
96
97
        $bzid = $this->params->get($param);
98
        $team = Player::getFromBZID($bzid)->getTeam();
99
100
        $teamName = ($team->isValid()) ? preg_replace("/&[^\s]*;/", "", $team->getName()) : '';
101
102
        return new JsonResponse(array(
103
            // API v1 legacy support
104
            "bzid" => $bzid,     // Replaced with "team_name" in API v2+
105
            "team" => $teamName, // Replaced with "player_bzid" in API v2+
106
107
            // API v2+
108
            "player_bzid" => $bzid,
109
            "team_id"     => $team->getId(),
110
            "team_name"   => $teamName,
111
        ));
112
    }
113
114
    public function teamNameDumpAction()
115
    {
116
        if ($this->version < 1) {
117
            throw new BadRequestException();
118
        }
119
120
        // Create an array to store all teams and the BZIDs
121
        $teamArray = array();
122
123
        foreach (Team::getTeams() as $team) {
124
            $memberList = "";
125
126
            foreach ($team->getMembers() as $member) {
127
                $memberList .= $member->getBZID() . ",";
128
            }
129
130
            $teamName    = preg_replace("/&[^\s]*;/", "", $team->getName());
131
            $teamID      = $team->getId();
132
            $teamMembers = rtrim($memberList, ",");
133
134
            $teamArray[] = array(
135
                // API v1 legacy support
136
                "team"    => $teamName,
137
                "members" => $teamMembers,
138
139
                // API v2+
140
                "team_id"      => $teamID,
141
                "team_name"    => $teamName,
142
                "team_members" => $teamMembers
143
            );
144
        }
145
146
        return new JsonResponse(array(
147
            // API v1 legacy support
148
            "teamDump" => &$teamArray,
149
150
            // API v2+
151
            "team_list" => &$teamArray
152
        ));
153
    }
154
155
    public function matchReportAction(Logger $log, Request $request)
156
    {
157
        $log->addNotice("Match data received from " . $request->getClientIp());
158
159
        $matchType = $this->params->get('matchType', Match::OFFICIAL);
160
161
        $teamOneBZIDs = $this->params->get('teamOnePlayers');
162
        $teamTwoBZIDs = $this->params->get('teamTwoPlayers');
163
164
        $teamOnePlayers = $this->bzidsToIdArray($teamOneBZIDs);
165
        $teamTwoPlayers = $this->bzidsToIdArray($teamTwoBZIDs);
166
167
        if (Match::OFFICIAL === $matchType) {
168
            $teamOne = $this->getTeam($teamOnePlayers);
169
            $teamTwo = $this->getTeam($teamTwoPlayers);
170
171
            // If we fail to get the the team ID for either the teams or both reported teams are the same team, we cannot
172
            // report the match due to it being illegal.
173
174
            // An invalid team could be found in either or both teams, so we need to check both teams and log the match
175
            // failure respectively.
176
            $error = true;
177
            if (!$teamOne->isValid()) {
178
                $log->addNotice("The BZIDs ($teamOneBZIDs) were not found on the same team. Match invalidated.");
179
            } elseif (!$teamTwo->isValid()) {
180
                $log->addNotice("The BZIDs ($teamTwoBZIDs) were not found on the same team. Match invalidated.");
181
            } else {
182
                $error = false;
183
            }
184
185
            if ($error) {
186
                throw new ForbiddenException("An invalid player was found during the match. Please message a referee to manually report the match.");
187
            }
188
189
            if ($teamOne->isSameAs($teamTwo)) {
190
                $log->addNotice("The '" . $teamOne->getName() . "' team played against each other in an official match. Match invalidated.");
191
                throw new ForbiddenException("Holy sanity check, Batman! The same team can't play against each other in an official match.");
192
            }
193
        }
194
195
        $map = Map::fetchFromAlias($this->params->get('mapPlayed'));
196
197
        $match = Match::enterMatch(
198
            (isset($teamOne)) ? $teamOne->getId() : null,
199
            (isset($teamTwo)) ? $teamTwo->getId() : null,
200
            $this->params->get('teamOneWins'),
201
            $this->params->get('teamTwoWins'),
202
            $this->params->get('duration'),
203
            null,
204
            $this->params->get('matchTime'),
205
            $teamOnePlayers,
206
            $teamTwoPlayers,
207
            $this->params->get('server'),
208
            $this->params->get('port'),
209
            $this->params->get('replayFile'),
210
            $map->getId(),
211
            $this->params->get('matchType'),
212
            $this->params->get('teamOneColor'),
213
            $this->params->get('teamTwoColor')
214
        );
215
216
        $log->addNotice("Match reported automatically", array(
217
            'winner' => array(
218
                'name'  => $match->getWinner()->getName(),
219
                'score' => $match->getScore($match->getWinner()),
220
            ),
221
            'loser' => array(
222
                'name'  => $match->getLoser()->getName(),
223
                'score' => $match->getScore($match->getLoser())
224
            ),
225
            'eloDiff' => $match->getEloDiff(),
226
            'map'     => $map->getName()
227
        ));
228
229
        // Output the match stats that will be sent back to BZFS
230
        return $match->getName();
231
    }
232
233
    /**
234
     * Convert a comma-separated list of bzids to player IDs so we can pass
235
     * them to Match::enterMatch()
236
     *
237
     * @param  string $players A comma-separated list of BZIDs
238
     * @return int[]  A list of Player IDs
239
     */
240
    private function bzidsToIdArray($players)
241
    {
242
        $players = explode(',', $players);
243
244
        foreach ($players as &$player) {
245
            $player = Player::getFromBZID($player)->getId();
246
        }
247
248
        return $players;
249
    }
250
251
    /**
252
     * Queries the database to get the team which a conversation of players belong to
253
     *
254
     * @param  int[] $players The IDs of players
255
     * @return Team  The team
256
     */
257
    private function getTeam($players)
258
    {
259
        $team = null;
260
261
        foreach ($players as $id) {
262
            $player = Player::get($id);
263
264
            if ($player->isTeamless()) {
265
                return Team::invalid();
266
            } elseif ($team == null) {
267
                $team = $player->getTeam();
268
            } elseif ($team->getId() != $player->getTeam()->getId()) {
269
                // This player is on a different team from the previous player!
270
                return Team::invalid();
271
            }
272
        }
273
274
        return $team;
275
    }
276
277
    /**
278
     * {@inheritdoc}
279
     */
280
    protected static function getLogChannel()
281
    {
282
        return 'api';
283
    }
284
}
285