• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / iznik-server / 5e9e4b03-c3c5-45e7-aaa1-3908a8f073e5

29 Nov 2024 12:31PM UTC coverage: 92.434% (-0.2%) from 92.626%
5e9e4b03-c3c5-45e7-aaa1-3908a8f073e5

push

circleci

edwh
WIP Stripe subscriptions

0 of 4 new or added lines in 1 file covered. (0.0%)

10 existing lines in 1 file now uncovered.

25474 of 27559 relevant lines covered (92.43%)

31.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.08
/include/API.php
1
<?php
2

3
namespace Freegle\Iznik;
4

5

6
require_once(IZNIK_BASE . '/include/db.php');
1✔
7
global $dbhr, $dbhm;
1✔
8

9
use GeoIp2\Database\Reader;
10

11
class API
12
{
13
    public static function headers() {
14
        $call = array_key_exists('call', $_REQUEST) ? $_REQUEST['call'] : NULL;
318✔
15
        $type = array_key_exists('type', $_REQUEST) ? $_REQUEST['type'] : 'GET';
318✔
16

17
        // We allow anyone to use our API.
18
        //
19
        // Suppress errors on the header command for UT
20
        if (!(($call == 'image' || $call == 'profile') && $type == 'GET')) {
318✔
21
            # For images we'll set the content type later.
22
            @header('Content-type: application/json');
317✔
23
        }
24

25
        // Access-Control-Allow-Origin not now added by nginx.
26
        @header('Access-Control-Allow-Origin: *');
318✔
27
        @header('Access-Control-Allow-Headers: ' . (array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER) ? $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] : "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Iznik-PHP-Session")); // X-HTTP-Method-Override not needed
318✔
28
        @header('Access-Control-Allow-Credentials: true');
318✔
29
        @header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
318✔
30
    }
31

32
    public static function call()
33
    {
34
        global $dbhr, $dbhm;
318✔
35

36
        $ip = Utils::presdef('REMOTE_ADDR', $_SERVER, '');
318✔
37

38
        if (file_exists("/tmp/noheaders-$ip")) {
318✔
39
            echo json_encode(array('ret' => 1, 'status' => 'Not  logged in'));
×
40
            exit(0);
×
41
        }
42

43
        $scriptstart = microtime(true);
318✔
44

45
        $entityBody = file_get_contents('php://input');
318✔
46

47
        if ($entityBody) {
318✔
48
            $parms = json_decode($entityBody, true);
×
49
            if (json_last_error() == JSON_ERROR_NONE) {
×
50
                # We have been passed parameters in JSON.
51
                foreach ($parms as $parm => $val) {
×
52
                    $_REQUEST[$parm] = $val;
×
53
                }
54
            } else {
55
                # In some environments (e.g. PHP-FPM 7.2) parameters passed as an encoded form for PUT aren't parsed correctly,
56
                # probably because we have a rewrite that adds a parameter to the URL, and PHP-FPM doesn't want to have both
57
                # URL and form parameters.
58
                #
59
                # This may be a bug or a feature, but it messes us up.  So decode anything we can find that has not already
60
                # been decoded by our interpreter (if it did it, it's likely to be better).
61
                #
62
                # We needed this code when the app didn't contain the use of HTTP_X_HTTP_METHOD_OVERRIDE, and it's useful
63
                # anyway in case the client forgets.
64
                parse_str($entityBody, $params);
×
65
                foreach ($params as $key => $val) {
×
66
                    if (!array_key_exists($key, $_REQUEST)) {
×
67
                        $_REQUEST[$key] = $val;
×
68
                    }
69
                }
70
            }
71
        }
72

73
        if (array_key_exists('REQUEST_METHOD', $_SERVER)) {
318✔
74
            $_SERVER['REQUEST_METHOD'] = strtoupper($_SERVER['REQUEST_METHOD']);
318✔
75
            $_REQUEST['type'] = $_SERVER['REQUEST_METHOD'];
318✔
76
        }
77

78
        if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER)) {
318✔
79
            # Used by Backbone's emulateHTTP to work around servers which don't handle verbs like PATCH very well.
80
            #
81
            # We use this because when we issue a PATCH we don't seem to be able to get the body parameters.
82
            $_REQUEST['type'] = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
1✔
83
            #error_log("Request method override to {$_REQUEST['type']}");
84
        }
85

86
        // Record timing.
87
        list ($tusage, $rusage) = API::requestStart();
318✔
88

89
        API::headers();
318✔
90

91
        // @codeCoverageIgnoreStart
92
        if (file_exists(IZNIK_BASE . '/http/maintenance_on.html')) {
93
            echo json_encode(array('ret' => 111, 'status' => 'Down for maintenance'));
94
            exit(0);
95
        }
96
        // @codeCoverageIgnoreEnd
97

98
        $includetime = microtime(true) - $scriptstart;
318✔
99

100
        # All API calls come through here.
101
        #error_log("Request " . var_export($_REQUEST, TRUE));
102
        #error_log("Server " . var_export($_SERVER, TRUE));
103

104
        if (Utils::pres('HTTP_X_REAL_IP', $_SERVER)) {
318✔
105
            # We jump through hoops to get the real IP address. This is one of them.
106
            $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_REAL_IP'];
×
107
        }
108

109
        if (array_key_exists('model', $_REQUEST)) {
318✔
110
            # Used by Backbone's emulateJSON to work around servers which don't handle requests encoded as
111
            # application/json.
112
            $_REQUEST = array_merge($_REQUEST, json_decode($_REQUEST['model'], true));
1✔
113
            unset($_REQUEST['model']);
1✔
114
        }
115

116
        # Include the API call
117
        $call = Utils::pres('call', $_REQUEST);
318✔
118

119
        if ($call) {
318✔
120
            $fn = IZNIK_BASE . '/http/api/' . $call . '.php';
318✔
121
            if (file_exists($fn)) {
318✔
122
                require_once($fn);
311✔
123
            }
124
        }
125

126
        if (Utils::presdef('type', $_REQUEST, null) == 'OPTIONS') {
318✔
127
            # We don't bother returning different values for different calls.
128
            http_response_code(204);
1✔
129
            @header('Allow: POST, GET, DELETE, PUT');
1✔
130
            @header('Access-Control-Allow-Methods:  POST, GET, DELETE, PUT');
1✔
131
        } else {
132
            # Actual API calls
133
            $ret = array('ret' => 1000, 'status' => 'Invalid API call');
317✔
134
            $t = microtime(true);
317✔
135

136
            # We wrap the whole request in a retry handler.  This is so that we can deal with errors caused by
137
            # conflicts within the Percona cluster.
138
            $apicallretries = 0;
317✔
139

140
            # This is an optimisation for User.php.
141
            if (session_status() !== PHP_SESSION_NONE) {
317✔
142
                $_SESSION['modorowner'] = Utils::presdef('modorowner', $_SESSION, []);
317✔
143
            }
144

145
            $encoded_ret = NULL;
317✔
146

147
            do {
148
                if (Utils::presdef('type', $_REQUEST, null) != 'GET') {
317✔
149
                    # Check that we're not posting from a blocked country.
150
                    try {
151
                        $reader = new Reader(MMDB);
245✔
152
                        $ip = Utils::presdef('REMOTE_ADDR', $_SERVER, null);
245✔
153

154
                        if ($ip) {
245✔
155
                            $record = $reader->country($ip);
236✔
156
                            $country = $record->country->name;
×
157
                            # Failed to look it up.
158
                            $countries = $dbhr->preQuery("SELECT * FROM spam_countries WHERE country = ?;", [$country]);
×
159
                            foreach ($countries as $country) {
9✔
160
                                error_log("Block post from {$country['country']} " . var_export($_REQUEST, true));
×
161
                                $encoded_ret = json_encode(array('ret' => 0, 'status' => 'Success'));
×
162
                                echo $encoded_ret;
×
163
                                break 2;
×
164
                            }
165
                        }
166
                    } catch (\Exception $e) {
236✔
167
                        // Don't log to Sentry - this can happen validly for some IPs (e.g. on CircleCI).
168
                    }
169
                }
170

171
                # Duplicate protection.  We upload multiple images so don't protect against those.
172
                if ((DUPLICATE_POST_PROTECTION > 0) &&
317✔
173
                    array_key_exists('REQUEST_METHOD', $_SERVER) &&
317✔
174
                    ((Utils::presdef('type', $_REQUEST, null) == 'POST' || Utils::presdef('type', $_REQUEST, null) == 'PUT')) &&
317✔
175
                    $call != 'image') {
317✔
176
                    # We want to make sure that we don't get duplicate POST requests within the same session.  We can't do this
177
                    # using information stored in the session because when Redis is used as the session handler, there is
178
                    # no session locking, and therefore two requests in quick succession could be allowed.  So instead
179
                    # we use Redis directly with a roll-your-own mutex.  We use the IP address as a key - if there are
180
                    # multiple sessions from the same IP then we might get false positives/negatives.
181
                    $reqData = $_REQUEST;
199✔
182
                    unset($reqData['requestid']);
199✔
183
                    $reqData['call'] = preg_replace('/(\?|&)requestid=[0-9]+?/', '', $reqData['call']);
199✔
184
                    $url = preg_replace('/(\?|&)requestid=[0-9]+?/', '', $_SERVER['REQUEST_URI']);
199✔
185
                    $req = $url . serialize($reqData);
199✔
186
                    $ip = Utils::presdef('REMOTE_ADDR', $_SERVER, NULL);
199✔
187
                    $lockkey = "POST_LOCK_$ip";
199✔
188
                    $datakey = "POST_DATA_$ip";
199✔
189
                    $uid = uniqid('', true);
199✔
190
                    $predis = new \Redis();
199✔
191
                    $predis->pconnect(REDIS_CONNECT);
199✔
192

193
                    # Get a lock.
194
                    $start = time();
199✔
195
                    $count = 0;
199✔
196

197
                    do {
198
                        $rc = $predis->setNx($lockkey, $uid);
199✔
199

200
                        if ($rc) {
199✔
201
                            # We managed to set it.  Ideally we would set an expiry time to make sure that if we got
202
                            # killed right now, this session wouldn't hang.  But that's an extra round trip on each
203
                            # API call, and the worst case would be a single session hanging, which we can live with.
204

205
                            # Sound out the last POST.
206
                            $last = $predis->get($datakey);
199✔
207

208
                            # Some actions are ok, so we exclude those.
209
                            if (!in_array($call, ['session', 'correlate', 'chatrooms', 'upload']) &&
199✔
210
                                $last ==  $req) {
199✔
211
                                # The last POST request was the same.  So this is a duplicate.
212
                                $predis->del($lockkey);
1✔
213
                                $ret = array(
1✔
214
                                    'ret' => 999,
1✔
215
                                    'text' => 'Duplicate request - rejected.',
1✔
216
                                    'data' => $_REQUEST
1✔
217
                                );
1✔
218
                                $encoded_ret = json_encode($ret);
1✔
219
                                echo $encoded_ret;
1✔
220
                                break 2;
1✔
221
                            }
222

223
                            # The last request wasn't the same.  Save this one.
224
                            $predis->set($datakey, $req);
199✔
225
                            $predis->expire($datakey, DUPLICATE_POST_PROTECTION);
199✔
226

227
                            # We're good to go - release the lock.
228
                            $predis->del($lockkey);
199✔
229
                            break;
199✔
230
                            // @codeCoverageIgnoreStart
231
                        } else {
232
                            # We didn't get the lock - another request for this session must have it.
233
                            $count++;
234
                            #error_log("Sleep for duplicate POST lock key $lockkey attempt $count, waited " . (time() - $start));
235
                            usleep(100000);
236
                        }
237
                    } while (time() < $start + 45);
238
                    // @codeCoverageIgnoreEnd
239
                }
240

241
                try {
242
                    # Each call is inside a file with a suitable name.
243
                    #
244
                    # call_user_func doesn't scale well on multicores with some versions of PHP, so we need can't figure out the function from
245
                    # the call name - use a switch instead.
246
                    switch ($call) {
247
                        case 'abtest':
317✔
248
                            $ret = abtest();
1✔
249
                            break;
1✔
250
                        case 'activity':
316✔
251
                            $ret = activity();
1✔
252
                            break;
1✔
253
                        case 'authority':
315✔
254
                            $ret = authority();
1✔
255
                            break;
1✔
256
                        case 'address':
314✔
257
                            $ret = address();
3✔
258
                            break;
3✔
259
                        case 'alert':
312✔
260
                            $ret = alert();
1✔
261
                            break;
1✔
262
                        case 'adview':
311✔
263
                            # Remove after 2021-05-05.
264
                            $ret = [
×
265
                                'ret' => 0,
×
266
                                'status' => 'Success',
×
267
                                'data' => []
×
268
                            ];
×
269
                            break;
×
270
                        case 'admin':
311✔
271
                            $ret = admin();
3✔
272
                            break;
3✔
273
                        case 'config':
309✔
274
                            $ret = config();
1✔
275
                            break;
1✔
276
                        case 'changes':
308✔
277
                            $ret = changes();
1✔
278
                            break;
1✔
279
                        case 'dashboard':
308✔
280
                            $ret = dashboard();
5✔
281
                            break;
5✔
282
                        case 'error':
305✔
283
                            $ret = error();
1✔
284
                            break;
1✔
285
                        case 'export':
304✔
286
                            $ret = export();
1✔
287
                            break;
1✔
288
                        case 'exception':
303✔
289
                            # For UT
290
                            throw new \Exception();
1✔
291
                        case 'image':
303✔
292
                            $ret = image();
26✔
293
                            break;
26✔
294
                        case 'isochrone':
283✔
295
                            $ret = isochrone();
2✔
296
                            break;
2✔
297
                        case 'jobs':
283✔
298
                            $ret = jobs();
1✔
299
                            break;
1✔
300
                        case 'profile':
282✔
301
                            $ret = profile();
1✔
302
                            break;
1✔
303
                        case 'socialactions':
281✔
304
                            $ret = socialactions();
×
305
                            break;
×
306
                        case 'messages':
281✔
307
                            $ret = messages();
25✔
308
                            break;
25✔
309
                        case 'message':
267✔
310
                            $ret = message();
74✔
311
                            break;
74✔
312
                        case 'invitation':
206✔
313
                            $ret = invitation();
1✔
314
                            break;
1✔
315
                        case 'item':
205✔
316
                            $ret = item();
4✔
317
                            break;
4✔
318
                        case 'usersearch':
201✔
319
                            $ret = usersearch();
1✔
320
                            break;
1✔
321
                        case 'memberships':
200✔
322
                            $ret = memberships();
30✔
323
                            break;
30✔
324
                        case 'merge':
175✔
325
                            $ret = merge();
2✔
326
                            break;
2✔
327
                        case 'spammers':
173✔
328
                            $ret = spammers();
4✔
329
                            break;
4✔
330
                        case 'session':
170✔
331
                            $ret = session();
50✔
332
                            break;
50✔
333
                        case 'group':
135✔
334
                            $ret = group();
9✔
335
                            break;
9✔
336
                        case 'groups':
126✔
337
                            $ret = groups();
1✔
338
                            break;
1✔
339
                        case 'communityevent':
125✔
340
                            $ret = communityevent();
2✔
341
                            break;
2✔
342
                        case 'domains':
123✔
343
                            $ret = domains();
1✔
344
                            break;
1✔
345
                        case 'locations':
122✔
346
                            $ret = locations();
5✔
347
                            break;
5✔
348
                        case 'logo':
117✔
349
                            $ret = logo();
1✔
350
                            break;
1✔
351
                        case 'modconfig':
116✔
352
                            $ret = modconfig();
10✔
353
                            break;
10✔
354
                        case 'stdmsg':
113✔
355
                            $ret = stdmsg();
3✔
356
                            break;
3✔
357
                        case 'bulkop':
110✔
358
                            $ret = bulkop();
4✔
359
                            break;
4✔
360
                        case 'comment':
106✔
361
                            $ret = comment();
4✔
362
                            break;
4✔
363
                        case 'user':
102✔
364
                            $ret = user();
29✔
365
                            break;
29✔
366
                        case 'chatrooms':
77✔
367
                            $ret = chatrooms();
19✔
368
                            break;
19✔
369
                        case 'chatmessages':
71✔
370
                            $ret = chatmessages();
16✔
371
                            break;
16✔
372
                        case 'poll':
55✔
373
                            $ret = poll();
2✔
374
                            break;
2✔
375
                        case 'request':
53✔
376
                            $ret = request();
1✔
377
                            break;
1✔
378
                        case 'shortlink':
52✔
379
                            $ret = shortlink();
2✔
380
                            break;
2✔
381
                        case 'stories':
50✔
382
                            $ret = stories();
3✔
383
                            break;
3✔
384
                        case 'donations':
48✔
385
                            $ret = donations();
2✔
386
                            break;
2✔
387
                        case 'giftaid':
46✔
388
                            $ret = giftaid();
4✔
389
                            break;
4✔
390
                        case 'status':
42✔
391
                            $ret = status();
1✔
392
                            break;
1✔
393
                        case 'volunteering':
41✔
394
                            $ret = volunteering();
4✔
395
                            break;
4✔
396
                        case 'logs':
37✔
397
                            $ret = logs();
1✔
398
                            break;
1✔
399
                        case 'newsfeed':
36✔
400
                            $ret = newsfeed();
14✔
401
                            break;
14✔
402
                        case 'noticeboard':
24✔
403
                            $ret = noticeboard();
3✔
404
                            break;
3✔
405
                        case 'notification':
22✔
406
                            $ret = notification();
3✔
407
                            break;
3✔
408
                        case 'mentions':
19✔
409
                            $ret = mentions();
1✔
410
                            break;
1✔
411
                        case 'microvolunteering':
18✔
412
                            $ret = microvolunteering();
5✔
413
                            break;
5✔
414
                        case 'team':
13✔
415
                            $ret = team();
1✔
416
                            break;
1✔
417
                        case 'tryst':
12✔
418
                            $ret = tryst();
4✔
419
                            break;
4✔
420
                        case 'src':
8✔
421
                            $ret = src();
1✔
422
                            break;
1✔
423
                        case 'visualise':
7✔
424
                            $ret = visualise();
1✔
425
                            break;
1✔
426
                        case 'stripecreateintent':
6✔
427
                            $ret = stripecreateintent();
×
428
                            break;
×
429
                        case 'stripecreatesubscription':
6✔
UNCOV
430
                            $ret = stripecreatesubscription();
×
UNCOV
431
                            break;
×
432
                        case 'echo':
6✔
433
                            $ret = array_merge($_REQUEST, $_SERVER);
2✔
434
                            break;
2✔
435
                        case 'DBexceptionWork':
4✔
436
                            # For UT
437
                            if ($apicallretries < 2) {
1✔
438
                                error_log("Fail DBException $apicallretries");
1✔
439
                                throw new DBException();
1✔
440
                            }
441

442
                            break;
1✔
443
                        case 'DBexceptionFail':
4✔
444
                            # For UT
445
                            throw new DBException();
1✔
446
                        case 'DBleaveTrans':
3✔
447
                            # For UT
448
                            $dbhm->beginTransaction();
1✔
449

450
                            break;
1✔
451
                    }
452

453
                    # If we get here, everything worked.
454
                    if ($call == 'upload') {
317✔
455
                        # Output is handled within the lib.
456
                    } else {
457
                        if (Utils::pres('redirectto', $ret)) {
317✔
458
                            header("Location: {$ret['redirectto']}", true, 302);
1✔
459
                        } else if (Utils::pres('img', $ret)) {
317✔
460
                            # This is an image we want to output.  Can cache forever - if an image changes it would get a new id
461
                            @header('Content-Type: image/jpeg');
×
UNCOV
462
                            @header('Content-Length: ' . strlen($ret['img']));
×
UNCOV
463
                            @header('Cache-Control: max-age=5360000');
×
UNCOV
464
                            print $ret['img'];
×
465
                        } else {
466
                            # This is a normal API call.  Add profiling info.
467
                            $duration = (microtime(true) - $scriptstart);
317✔
468
                            $ret['call'] = $call;
317✔
469
                            $ret['type'] = Utils::presdef('type', $_REQUEST, null);
317✔
470
                            $ret['session'] = session_id();
317✔
471
                            $ret['duration'] = $duration;
317✔
472
                            $ret['cpucost'] = API::getCpuUsage($tusage, $rusage);
317✔
473
                            $ret['dbwaittime'] = $dbhr->getWaitTime() + $dbhm->getWaitTime();
317✔
474
                            $ret['includetime'] = $includetime;
317✔
475
                            //                $ret['remoteaddr'] = Utils::presdef('REMOTE_ADDR', $_SERVER, '-');
476
                            //                $ret['_server'] = $_SERVER;
477

478
                            Utils::filterResult($ret);
317✔
479

480
                            $encoded_ret = json_encode($ret, JSON_PARTIAL_OUTPUT_ON_ERROR);
317✔
481
                            echo $encoded_ret;
317✔
482

483
                            if ($duration > 5000) {
317✔
484
                                # Slow call.
UNCOV
485
                                $stamp = microtime(true);
×
UNCOV
486
                                error_log("Slow API call for user " . Utils::presdef('id', $_SESSION, NULL) . " $call stamp $stamp");
×
UNCOV
487
                                file_put_contents("/tmp/iznik.slowapi.$stamp", "User # " . Utils::presdef('id', $_SESSION, NULL) . " request " . var_export($_REQUEST, true) . " response " . var_export($ret, TRUE));
×
488
                            }
489
                        }
490
                    }
491

492
                    if ($apicallretries > 0) {
317✔
493
                        error_log("API call $call worked after $apicallretries");
1✔
494
                    }
495

496
                    break;
317✔
497
                } catch (\Exception $e) {
1✔
498
                    # This is our retry handler.
499
                    if ($e instanceof DBException) {
1✔
500
                        # This is a DBException.  We want to retry, which means we just go round the loop
501
                        # again.
502
                        error_log(
1✔
503
                            "DB Exception try $apicallretries," . $e->getMessage() . ", " . $e->getTraceAsString()
1✔
504
                        );
1✔
505
                        $apicallretries++;
1✔
506

507
                        if ($apicallretries >= API_RETRIES) {
1✔
508
                            if ($call != 'DBexceptionFail') {
1✔
509
                                # Don't log deliberate exceptions in UT.
UNCOV
510
                                \Sentry\captureException($e);
×
511
                            }
512

513
                            $ret = [
1✔
514
                                'ret' => 997,
1✔
515
                                'status' => 'DB operation failed after retry',
1✔
516
                                'exception' => $e->getMessage()
1✔
517
                            ];
1✔
518
                            $encoded_ret = json_encode($ret);
1✔
519
                            echo $encoded_ret;
1✔
520
                        }
521
                    } else {
522
                        # Something else.
523
                        if ($call != 'exception') {
1✔
524
                            # Don't log deliberate exceptions in UT.
UNCOV
525
                            \Sentry\captureException($e);
×
526
                        }
527

528
                        error_log(
1✔
529
                            "Uncaught exception at " . $e->getFile() . " line " . $e->getLine() . " " . $e->getMessage()
1✔
530
                        );
1✔
531
                        $ret = ['ret' => 998, 'status' => 'Unexpected error', 'exception' => $e->getMessage()];
1✔
532
                        $encoded_ret = json_encode($ret);
1✔
533
                        echo $encoded_ret;
1✔
534
                        break;
1✔
535
                    }
536

537
                    # Make sure the duplicate POST detection doesn't throw us.
538
                    $_REQUEST['retry'] = uniqid('', true);
1✔
539
                }
540
            } while ($apicallretries < API_RETRIES);
1✔
541

542
            if (BROWSERTRACKING && (Utils::presdef('type', $_REQUEST, null) != 'GET') &&
317✔
543
                (gettype($ret) == 'array' && !array_key_exists('nolog', $ret))) {
317✔
544
                # Save off the API call and result, except for the (very frequent) event tracking calls.  Don't
545
                # save GET calls as they don't change the DB and there are a lot of them.
546
                #
547
                # Beanstalk has a limit on the size of job that it accepts; no point trying to log absurdly large
548
                # API requests.
549
                $r = $_REQUEST;
243✔
550
                $headers = Session::getallheaders();
243✔
551
                $r['headers'] = $headers;
243✔
552
                $req = json_encode($r);
243✔
553
                $rsp = $encoded_ret;
243✔
554

555
                if (strlen($req) + strlen($rsp) > 180000) {
243✔
556
                    $req = substr($req, 0, 1000);
1✔
557
                    $rsp = substr($rsp, 0, 1000);
1✔
558
                }
559

560
                $sql = "INSERT INTO logs_api (`userid`, `ip`, `session`, `request`, `response`) VALUES (" .
243✔
561
                    (session_status() !== PHP_SESSION_NONE ? Utils::presdef('id', $_SESSION,'NULL') : 'NULL') .
243✔
562
                    ", '" . Utils::presdef('REMOTE_ADDR', $_SERVER, '') . "', " . $dbhr->quote(session_id()) .
243✔
563
                    ", " . $dbhr->quote($req) . ", " . $dbhr->quote($rsp) . ");";
243✔
564
                $dbhm->background($sql);
243✔
565
            }
566

567
            # Any outstanding transaction is a bug; force a rollback to avoid locks lasting beyond this call.
568
            if ($dbhm->inTransaction()) {
317✔
569
                $dbhm->rollBack();
1✔
570
            }
571

572
            if (session_status() !== PHP_SESSION_NONE) {
317✔
573
                if (Utils::presdef('type', $_REQUEST, null) != 'GET') {
317✔
574
                    # This might have changed things.
575
                    $_SESSION['modorowner'] = [];
245✔
576
                }
577

578
                # Update our last access time for this user.  his is used to return our
579
                # roster status in ChatRoom.php, and also for spotting idle members.
580
                #
581
                # Do this here, as we might not be logged in at the start if we had a persistent token but no PHP session.
582
                $id = Utils::pres('id', $_SESSION);
317✔
583
                $last = intval(Utils::presdef('lastaccessupdate', $_SESSION, 0));
317✔
584
                if ($id && (abs(time() - $last) > 600)) {
317✔
585
                    $dbhm->background("UPDATE users SET lastaccess = NOW() WHERE id = $id;");
241✔
586
                    $_SESSION['lastaccessupdate'] = time();
241✔
587
                }
588
            }
589
        }
590
    }
591

592
    public static function requestStart() {
593
        $dat = getrusage();
318✔
594
        return ([ microtime(true), $dat["ru_utime.tv_sec"]*1e6+$dat["ru_utime.tv_usec"] ]);
318✔
595
    }
596

597
    public static function getCpuUsage($tusage, $rusage) {
598
        $dat = getrusage();
317✔
599
        $dat["ru_utime.tv_usec"] = ($dat["ru_utime.tv_sec"]*1e6 + $dat["ru_utime.tv_usec"]) - $rusage;
317✔
600
        $time = (microtime(true) - $tusage) * 1000000;
317✔
601

602
        // cpu per request
603
        $cpu = $time > 0 ? $dat["ru_utime.tv_usec"] / $time / 1000 : 0;
317✔
604

605
        return $cpu;
317✔
606
    }
607
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc