This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* zKillboard |
||
3 | * Copyright (C) 2012-2015 EVE-KILL Team and EVSCO. |
||
4 | * |
||
5 | * This program is free software: you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU Affero General Public License as published by |
||
7 | * the Free Software Foundation, either version 3 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU Affero General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Affero General Public License |
||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
17 | */ |
||
18 | |||
19 | /** |
||
20 | * Various API helper functions for the website |
||
21 | */ |
||
22 | class Api |
||
23 | { |
||
24 | |||
25 | /** |
||
26 | * Checks a key for validity and KillLog access. |
||
27 | * |
||
28 | * @static |
||
29 | * @param $keyID int The keyID to be checked. |
||
30 | * @param $vCode string The vCode to be checked |
||
31 | * @return string A message, Success on success, otherwise an error. |
||
32 | */ |
||
33 | public static function checkAPI($keyID, $vCode) |
||
34 | { |
||
35 | $keyID = trim($keyID); |
||
36 | $vCode = trim($vCode); |
||
37 | if ($keyID == "" || $vCode == "") |
||
38 | return "Error, no keyID and/or vCode"; |
||
39 | $keyID = (int)$keyID; |
||
40 | if ($keyID == 0) { |
||
41 | return "Invalid keyID. Did you get the keyID and vCode mixed up?"; |
||
42 | } |
||
43 | |||
44 | $pheal = Util::getPheal($keyID, $vCode); |
||
45 | try |
||
46 | { |
||
47 | $result = $pheal->accountScope->APIKeyInfo(); |
||
48 | } |
||
49 | catch (Exception $e) |
||
50 | { |
||
51 | if (strlen($keyID) > 20) |
||
52 | return "Error, you might have mistaken keyid for the vcode"; |
||
53 | return "Error: " . $e->getCode() . " Message: " . $e->getMessage(); |
||
54 | } |
||
55 | |||
56 | $key = $result->key; |
||
57 | $accessMask = $key->accessMask; |
||
58 | $hasBits = self::hasBits($accessMask); |
||
59 | |||
60 | if (!$hasBits) { |
||
61 | return "Error, key does not have access to killlog, please modify key to add killlog access"; |
||
62 | } |
||
63 | return "success"; |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Adds a key to the database. |
||
68 | * |
||
69 | * @static |
||
70 | * @param int $keyID |
||
71 | * @param string $vCode |
||
72 | * @param null|string $label |
||
73 | * @return string |
||
74 | */ |
||
75 | public static function addKey($keyID, $vCode, $label = null) |
||
76 | { |
||
77 | $userID = User::getUserID(); |
||
78 | if ($userID == null) $userID = 0; |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
79 | |||
80 | $exists = Db::queryRow("SELECT userID, keyID, vCode FROM zz_api WHERE keyID = :keyID AND vCode = :vCode", array(":keyID" => $keyID, ":vCode" => $vCode), 0); |
||
81 | if ($exists == null) { |
||
82 | // Insert the api key |
||
83 | Db::execute("replace into zz_api (userID, keyID, vCode, label) VALUES (:userID, :keyID, :vCode, :label)", array(":userID" => $userID, ":keyID" => $keyID, ":vCode" => $vCode, ":label" => $label)); |
||
84 | } else if ($exists["userID"] == 0) { |
||
85 | // Someone already gave us this key anonymously, give it to this user |
||
86 | Db::execute("UPDATE zz_api SET userID = :userID, label = :label WHERE keyID = :keyID", array(":userID" => $userID, ":label" => $label, ":keyID" => $keyID)); |
||
87 | return "keyID $keyID previously existed in our database but has now been assigned to you."; |
||
88 | } else { |
||
89 | return "keyID $keyID is already in the database..."; |
||
90 | } |
||
91 | |||
92 | $pheal = Util::getPheal($keyID, $vCode); |
||
93 | $result = $pheal->accountScope->APIKeyInfo(); |
||
94 | $key = $result->key; |
||
95 | $keyType = $key->type; |
||
96 | |||
97 | if ($keyType == "Account") $keyType = "Character"; |
||
98 | |||
99 | $ip = IP::get(); |
||
100 | |||
101 | Log::log("API: $keyID has been added. Type: $keyType ($ip)"); |
||
102 | return "Success, your $keyType key has been added."; |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Deletes a key owned by the currently logged in user. |
||
107 | * |
||
108 | * @static |
||
109 | * @param $keyID int |
||
110 | * @return string |
||
111 | */ |
||
112 | public static function deleteKey($keyID) |
||
113 | { |
||
114 | $userID = user::getUserID(); |
||
115 | Db::execute("DELETE FROM zz_api_characters WHERE keyID = :keyID", array(":keyID" => $keyID)); |
||
116 | Db::execute("DELETE FROM zz_api WHERE userID = :userID AND keyID = :keyID", array(":userID" => $userID, ":keyID" => $keyID)); |
||
117 | return "$keyID has been deleted"; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Returns a list of keys owned by the currently logged in user. |
||
122 | * |
||
123 | * @static |
||
124 | * @param $userID int |
||
125 | * @return array Returns |
||
126 | */ |
||
127 | public static function getKeys($userID) |
||
128 | { |
||
129 | if(!isset($userID)) |
||
130 | $userID = user::getUserID(); |
||
131 | |||
132 | $result = Db::query("SELECT keyID, vCode, label, lastValidation, errorCode FROM zz_api WHERE userID = :userID order by keyID", array(":userID" => $userID), 0); |
||
133 | return $result; |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * Returns an array of character keys. |
||
138 | * |
||
139 | * @static |
||
140 | * @param $userID int |
||
141 | * @return array Returns |
||
142 | */ |
||
143 | public static function getCharacterKeys($userID) |
||
144 | { |
||
145 | $result = Db::query("select c.* from zz_api_characters c left join zz_api a on (c.keyID = a.keyID) where a.userID = :userID", array(":userID" => $userID), 0); |
||
146 | return $result; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Returns an array of the characters assigned to this user. |
||
151 | * |
||
152 | * @static |
||
153 | * @param $userID int |
||
154 | * @return array |
||
155 | */ |
||
156 | public static function getCharacters($userID) |
||
157 | { |
||
158 | $db = Db::query("SELECT characterID FROM zz_api_characters c left join zz_api a on (c.keyID = a.keyID) where userID = :userID", array(":userID" => $userID), 0); |
||
159 | $results = Info::addInfo($db); |
||
160 | return $results; |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Tests the access mask for KillLog access |
||
165 | * |
||
166 | * @static |
||
167 | * @param int $accessMask |
||
168 | * @return bool |
||
169 | */ |
||
170 | public static function hasBits($accessMask) |
||
171 | { |
||
172 | return ((int)($accessMask & 256) > 0); |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * API exception handling |
||
177 | * |
||
178 | * @static |
||
179 | * @param integer $keyID |
||
180 | * @param int $charID |
||
181 | * @param Exception $exception |
||
182 | * @return void |
||
183 | */ |
||
184 | public static function handleApiException($keyID, $charID, $exception) |
||
185 | { |
||
186 | $code = $exception->getCode(); |
||
187 | $message = $exception->getMessage(); |
||
188 | $clearCharacter = false; |
||
189 | $clearAllCharacters = false; |
||
190 | $clearApiEntry = false; |
||
191 | $updateCacheTime = false; |
||
192 | $demoteCharacter = false; |
||
193 | $cacheUntil = 0; |
||
194 | switch ($code) { |
||
195 | case 28: // Timeouts |
||
196 | case 904: // temp ban from ccp's api server |
||
197 | Db::execute("replace into zz_storage values ('ApiStop904', date_add(now(), interval 5 minute))"); |
||
198 | break; |
||
199 | |||
200 | case 403: |
||
201 | case 502: |
||
202 | case 503: // Service Unavailable - try again later |
||
203 | $cacheUntil = time() + 300; |
||
204 | $updateCacheTime = true; |
||
205 | break; |
||
206 | |||
207 | case 119: // Kills exhausted: retry after [{0}] |
||
208 | $cacheUntil = $exception->cached_until; |
||
209 | $updateCacheTime = true; |
||
210 | break; |
||
211 | |||
212 | case 120: // Expected beforeKillID [{0}] but supplied [{1}]: kills previously loaded. |
||
213 | $cacheUntil = $exception->cached_until; |
||
214 | $updateCacheTime = true; |
||
215 | break; |
||
216 | |||
217 | case 221: // Demote toon, illegal page access |
||
218 | $clearAllCharacters = true; |
||
219 | $clearApiEntry = true; |
||
220 | break; |
||
221 | |||
222 | case 220: |
||
223 | case 200: // Current security level not high enough. |
||
224 | // Typically happens when a key isn't a full API Key |
||
225 | $clearAllCharacters = true; |
||
226 | $clearApiEntry = true; |
||
227 | //$code = 203; // Force it to go away, no point in keeping this key |
||
228 | break; |
||
229 | |||
230 | case 522: |
||
231 | case 201: // Character does not belong to account. |
||
232 | // Typically caused by a character transfer |
||
233 | $clearCharacter = true; |
||
234 | break; |
||
235 | case 207: // Not available for NPC corporations. |
||
236 | case 209: |
||
237 | $demoteCharacter = true; |
||
238 | break; |
||
239 | |||
240 | case 222: // account has expired |
||
241 | $clearAllCharacters = true; |
||
242 | $clearApiEntry = true; |
||
243 | $cacheUntil = time() + (7 * 24 * 3600); // Try again in a week |
||
244 | break; |
||
245 | |||
246 | case 403: |
||
247 | case 211: // Login denied by account status |
||
248 | // Remove characters, will revalidate with next doPopulate |
||
249 | $clearAllCharacters = true; |
||
250 | $clearApiEntry = true; |
||
251 | break; |
||
252 | |||
253 | case 202: // API key authentication failure. |
||
254 | case 203: // Authentication failure - API is no good and will never be good again |
||
255 | case 204: // Authentication failure. |
||
256 | case 205: // Authentication failure (final pass). |
||
257 | case 210: // Authentication failure. |
||
258 | case 521: // Invalid username and/or password passed to UserData.LoginWebUser(). |
||
259 | $clearAllCharacters = true; |
||
260 | $clearApiEntry = true; |
||
261 | break; |
||
262 | |||
263 | case 500: // Internal Server Error (More CCP Issues) |
||
264 | case 520: // Unexpected failure accessing database. (More CCP issues) |
||
265 | case 404: // URL Not Found (CCP having issues...) |
||
266 | case 902: // Eve backend database temporarily disabled |
||
267 | $updateCacheTime = true; |
||
268 | $cacheUntil = time() + 3600; // Try again in an hour... |
||
269 | break; |
||
270 | |||
271 | case 0: // API Date could not be read / parsed, original exception (Something is wrong with the XML and it couldn't be parsed) |
||
272 | default: // try again in 5 minutes |
||
273 | Log::log("$keyID - Unhandled error - Code $code - $message"); |
||
274 | //$updateCacheTime = true; |
||
275 | $clearApiEntry = true; |
||
276 | //$cacheUntil = time() + 300; |
||
277 | break; |
||
278 | } |
||
279 | |||
280 | if ($demoteCharacter && $charID != 0) { |
||
281 | if (false === Db::execute("update zz_api_characters set isDirector = 'F' where characterID = :charID", array(":charID" => $charID), false)) { |
||
282 | $clearCharacter = true; |
||
283 | } |
||
284 | } |
||
285 | |||
286 | if ($clearCharacter && $charID != 0) { |
||
287 | Db::execute("delete from zz_api_characters where keyID = :keyID and characterID = :charID", array(":keyID" => $keyID, ":charID" => $charID)); |
||
288 | } |
||
289 | |||
290 | if ($clearAllCharacters) { |
||
291 | Db::execute("delete from zz_api_characters where keyID = :keyID", array(":keyID" => $keyID)); |
||
292 | } |
||
293 | |||
294 | if ($clearApiEntry) { |
||
295 | Db::execute("update zz_api set errorCode = :code where keyID = :keyID", array(":keyID" => $keyID, ":code" => $code)); |
||
296 | } |
||
297 | |||
298 | if ($updateCacheTime && $cacheUntil != 0 && $charID != 0) { |
||
299 | Db::execute("update zz_api_characters set cachedUntil = :cacheUntil where characterID = :charID", |
||
300 | array(":cacheUntil" => $cacheUntil, ":charID" => $charID)); |
||
301 | } |
||
302 | Db::execute("update zz_api_characters set errorCode = :code where keyID = :keyID and characterID = :charID", array(":keyID" => $keyID, ":charID" => $charID, ":code" => $code)); |
||
303 | } |
||
304 | |||
305 | public static function fetchApis() |
||
306 | { |
||
307 | global $baseDir; |
||
308 | |||
309 | Db::execute("delete from zz_api_characters where isDirector = ''"); // Minor cleanup |
||
310 | $fetchesPerSecond = (int) Storage::retrieve("APIFetchesPerSecond", 30); |
||
311 | $maxModulus = Db::queryField("select max(modulus) maxModulus from zz_api_characters", "maxModulus", array(), 0); |
||
312 | // If the fetchesPerSecond has changed we need to update the modulus on all rows to make sure everyone gets a turn |
||
313 | if (($maxModulus + 1) != $fetchesPerSecond) |
||
314 | { |
||
315 | Log::log("Updating modulus in zz_api_characters table..."); |
||
316 | Db::execute("update zz_api_characters set modulus = null"); |
||
317 | } |
||
318 | Db::execute("update zz_api_characters set modulus = (apiRowID % :modulus) where modulus is null", array(":modulus" => $fetchesPerSecond)); |
||
319 | |||
320 | for ($i = 0; $i < $fetchesPerSecond; $i++) |
||
321 | { |
||
322 | $command = "flock -w 60 $baseDir/cache/locks/preFetch.$i php5 $baseDir/cli.php apiFetchKillLog $i $fetchesPerSecond"; |
||
323 | $command = escapeshellcmd($command); |
||
324 | exec("$command >/dev/null 2>/dev/null &"); |
||
325 | } |
||
326 | } |
||
327 | |||
328 | public static function doApiSummary() |
||
329 | { |
||
330 | $lastActualKills = Db::queryField("select contents count from zz_storage where locker = 'actualKills'", "count", array(), 0); |
||
331 | $actualKills = Db::queryField("select count(*) count from zz_killmails where processed = 1", "count", array(), 0); |
||
332 | |||
333 | $lastTotalKills = Db::queryField("select contents count from zz_storage where locker = 'totalKills'", "count", array(), 0); |
||
334 | $totalKills = Db::queryField("select count(*) count from zz_killmails", "count", array(), 0); |
||
335 | |||
336 | Db::execute("replace into zz_storage (locker, contents) values ('totalKills', $totalKills)"); |
||
337 | Db::execute("replace into zz_storage (locker, contents) values ('actualKills', $actualKills)"); |
||
338 | Db::execute("delete from zz_storage where locker like '%KillsProcessed'"); |
||
339 | |||
340 | $actualDifference = number_format($actualKills - $lastActualKills, 0); |
||
341 | $totalDifference = number_format($totalKills - $lastTotalKills, 0); |
||
342 | |||
343 | Log::irc("|g|$actualDifference|n| mails processed | |g|$totalDifference|n| kills added"); |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * @param string $keyID string |
||
348 | * @param $charID int |
||
349 | * @param $killlog string |
||
350 | * @return int |
||
351 | */ |
||
352 | public static function processRawApi($keyID, $charID, $killlog) |
||
353 | { |
||
354 | $count = 0; |
||
355 | foreach ($killlog->kills as $kill) { |
||
356 | $killID = $kill->killID; |
||
357 | |||
358 | $json = json_encode($kill->toArray()); |
||
359 | $hash = Util::getKillHash(null, $kill); |
||
360 | |||
361 | $inDb = Db::queryField("select count(1) count from zz_killmails where killID = :killID", "count", array(":killID" => $killID), 0); |
||
362 | if ($inDb == 0) |
||
363 | { |
||
364 | $added = Db::execute("insert ignore into zz_killmails (killID, hash, source, kill_json) values (:killID, :hash, :source, :json)", |
||
365 | array(":killID" => $killID, ":hash" => $hash, ":source" => "keyID:$keyID", ":json" => $json)); |
||
366 | $count += $added; |
||
367 | } |
||
368 | } |
||
369 | return $count; |
||
370 | } |
||
371 | } |
||
372 |