Beispiel #1
0
 public static function call(OkapiRequest $request)
 {
     # Read the parameters.
     $acode = $request->get_parameter('acode');
     if ($acode === null) {
         throw new ParamMissing('acode');
     }
     if (strstr($acode, '|')) {
         throw new InvalidParam('acode', "Only a single A-code must be supplied.");
     }
     $langpref = $request->get_parameter('langpref');
     if (!$langpref) {
         $langpref = "en";
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         $fields = "name";
     }
     $forward_compatible = $request->get_parameter('forward_compatible');
     if (!$forward_compatible) {
         $forward_compatible = "true";
     }
     # Pass them all to the attributes method.
     $params = array('acodes' => $acode, 'langpref' => $langpref, 'fields' => $fields, 'forward_compatible' => $forward_compatible);
     $results = OkapiServiceRunner::call('services/attrs/attributes', new OkapiInternalRequest($request->consumer, $request->token, $params));
     $result = $results[$acode];
     if ($result === null) {
         /* Note, this can happen only when $forward_compatible is false. */
         throw new InvalidParam('acode', "Unknown A-code.");
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #2
0
 public static function call(OkapiRequest $request)
 {
     $search_assistant = new SearchAssistant($request);
     $search_assistant->prepare_common_search_params();
     $result = $search_assistant->get_common_search_result();
     return Okapi::formatted_response($request, $result);
 }
Beispiel #3
0
 static function call(OkapiRequest $request)
 {
     require_once 'log_images_common.inc.php';
     list($image_uuid, $log_internal_id) = LogImagesCommon::validate_image_uuid($request);
     $image_uuid_escaped = Db::escape_string($image_uuid);
     Db::execute('start transaction');
     $image_row = Db::select_row("\n            select id, node, url, local\n            from pictures\n            where uuid = '" . $image_uuid_escaped . "'\n        ");
     Db::execute("\n            delete from pictures where uuid = '" . $image_uuid_escaped . "'\n        ");
     # Remember that OCPL picture sequence numbers are always 1, and
     # OCDE sequence numbers may have gaps. So we do not need to adjust
     # any numbers after deleting from table 'pictures'.
     if (Settings::get('OC_BRANCH') == 'oc.de') {
         # OCDE does all the housekeeping by triggers
     } else {
         Db::execute("\n                INSERT INTO removed_objects (\n                    localID, uuid, type, removed_date, node\n                )\n                VALUES (\n                    " . $image_row['id'] . "\n                    '" . $image_uuid_escaped . "',\n                    6,\n                    NOW(),\n                    " . $image_row['node'] . "\n                )\n            ");
         # This will also update cache_logs.okapi_syncbase, so that replication
         # can output the updated log entry with one image less. For OCDE
         # that's done by DB trigges.
         Db::execute("\n                update cache_logs\n                set\n                    picturescount = greatest(0, picturescount - 1),\n                    last_modified = NOW()\n                where id = '" . Db::escape_string($log_internal_id) . "'\n            ");
     }
     Db::execute('commit');
     if ($image_row['local']) {
         $filename = basename($image_row['url']);
         unlink(Settings::get('IMAGES_DIR') . '/' . $filename);
     }
     $result = array('success' => true);
     return Okapi::formatted_response($request, $result);
 }
Beispiel #4
0
 public static function call(OkapiRequest $request)
 {
     # User is already verified (via OAuth), but we need to verify the
     # cache code (check if it exists). We will simply call a geocache method
     # on it - this will also throw a proper exception if it doesn't exist.
     $cache_code = $request->get_parameter('cache_code');
     if ($cache_code == null) {
         throw new ParamMissing('cache_code');
     }
     $geocache = OkapiServiceRunner::call('services/caches/geocache', new OkapiInternalRequest($request->consumer, $request->token, array('cache_code' => $cache_code, 'fields' => 'internal_id')));
     # watched
     if ($tmp = $request->get_parameter('watched')) {
         if (!in_array($tmp, array('true', 'false', 'unchanged'))) {
             throw new InvalidParam('watched', $tmp);
         }
         if ($tmp == 'true') {
             Db::execute("\n                    insert ignore into cache_watches (cache_id, user_id)\n                    values (\n                        '" . Db::escape_string($geocache['internal_id']) . "',\n                        '" . Db::escape_string($request->token->user_id) . "'\n                    );\n                ");
         } elseif ($tmp == 'false') {
             Db::execute("\n                    delete from cache_watches\n                    where\n                        cache_id = '" . Db::escape_string($geocache['internal_id']) . "'\n                        and user_id = '" . Db::escape_string($request->token->user_id) . "';\n                ");
         }
     }
     # ignored
     if ($tmp = $request->get_parameter('ignored')) {
         if (!in_array($tmp, array('true', 'false', 'unchanged'))) {
             throw new InvalidParam('ignored', $tmp);
         }
         if ($tmp == 'true') {
             Db::execute("\n                    insert ignore into cache_ignore (cache_id, user_id)\n                    values (\n                        '" . Db::escape_string($geocache['internal_id']) . "',\n                        '" . Db::escape_string($request->token->user_id) . "'\n                    );\n                ");
         } elseif ($tmp == 'false') {
             Db::execute("\n                    delete from cache_ignore\n                    where\n                        cache_id = '" . Db::escape_string($geocache['internal_id']) . "'\n                        and user_id = '" . Db::escape_string($request->token->user_id) . "'\n                ");
         }
     }
     $result = array('success' => true);
     return Okapi::formatted_response($request, $result);
 }
Beispiel #5
0
 public static function call(OkapiRequest $request)
 {
     $issue_id = $request->get_parameter('issue_id');
     if (!$issue_id) {
         throw new ParamMissing('issue_id');
     }
     if (!preg_match("/^[0-9]+\$/", $issue_id) || strlen($issue_id) > 6) {
         throw new InvalidParam('issue_id');
     }
     $cache_key = "apiref/issue#" . $issue_id;
     $result = Cache::get($cache_key);
     if ($result == null) {
         # Download the number of comments from GitHub Issue Tracker.
         try {
             $extra_headers = array();
             $extra_headers[] = "Accept: application/vnd.github.v3.html+json";
             $extra_headers[] = "User-Agent: https://github.com/opencaching/okapi/";
             if (Settings::get('GITHUB_ACCESS_TOKEN')) {
                 $extra_headers[] = "Authorization: token " . Settings::get('GITHUB_ACCESS_TOKEN');
             }
             $opts = array('http' => array('method' => "GET", 'timeout' => 2.0, 'header' => implode("\r\n", $extra_headers)));
             $context = stream_context_create($opts);
             $json = file_get_contents("https://api.github.com/repos/opencaching/okapi/issues/{$issue_id}", false, $context);
         } catch (ErrorException $e) {
             throw new BadRequest("Sorry, we could not retrieve issue stats from the GitHub site. " . "This is probably due to a temporary connection problem. Try again later or contact " . "us if this seems permanent.");
         }
         $doc = json_decode($json, true);
         $result = array('id' => $issue_id + 0, 'last_updated' => $doc['updated_at'], 'title' => $doc['title'], 'url' => $doc['html_url'], 'comment_count' => $doc['comments']);
         # On one hand, we want newly added comments to show up quickly.
         # On the other, we don't want OKAPI to spam GitHub with queries.
         # So it's difficult to choose the best timeout for this.
         Cache::set($cache_key, $result, 3600);
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #6
0
 public static function call(OkapiRequest $request)
 {
     $result = array();
     $result['site_url'] = Settings::get('SITE_URL');
     $result['okapi_base_url'] = $result['site_url'] . "okapi/";
     $result['site_name'] = Okapi::get_normalized_site_name();
     $result['okapi_revision'] = Okapi::$revision;
     return Okapi::formatted_response($request, $result);
 }
Beispiel #7
0
 public static function call(OkapiRequest $request)
 {
     $cachekey = "apisrv/stats";
     $result = Cache::get($cachekey);
     if (!$result) {
         $result = array('cache_count' => 0 + Db::select_value("\n                    select count(*) from caches where status in (1,2,3)\n                "), 'user_count' => 0 + Db::select_value("\n                    select count(*) from (\n                        select distinct user_id\n                        from cache_logs\n                        where\n                            type in (1,2,7)\n                            and " . (Settings::get('OC_BRANCH') == 'oc.pl' ? "deleted = 0" : "true") . "\n                        UNION DISTINCT\n                        select distinct user_id\n                        from caches\n                    ) as t;\n                "), 'apps_count' => 0 + Db::select_value("select count(*) from okapi_consumers;"), 'apps_active' => 0 + Db::select_value("\n                    select count(distinct s.consumer_key)\n                    from\n                        okapi_stats_hourly s,\n                        okapi_consumers c\n                    where\n                        s.consumer_key = c.`key`\n                        and s.period_start > date_add(now(), interval -30 day)\n                "));
         Cache::set($cachekey, $result, 86400);
         # cache it for one day
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #8
0
 public static function call(OkapiRequest $request)
 {
     $cache_code = $request->get_parameter('cache_code');
     if (!$cache_code) {
         throw new ParamMissing('cache_code');
     }
     if (strpos($cache_code, "|") !== false) {
         throw new InvalidParam('cache_code');
     }
     $langpref = $request->get_parameter('langpref');
     if (!$langpref) {
         $langpref = "en";
     }
     $langpref .= "|" . Settings::get('SITELANG');
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         $fields = "code|name|location|type|status";
     }
     $log_fields = $request->get_parameter('log_fields');
     if (!$log_fields) {
         $log_fields = "uuid|date|user|type|comment";
     }
     $lpc = $request->get_parameter('lpc');
     if (!$lpc) {
         $lpc = 10;
     }
     $attribution_append = $request->get_parameter('attribution_append');
     if (!$attribution_append) {
         $attribution_append = 'full';
     }
     $params = array('cache_codes' => $cache_code, 'langpref' => $langpref, 'fields' => $fields, 'attribution_append' => $attribution_append, 'lpc' => $lpc, 'log_fields' => $log_fields);
     $my_location = $request->get_parameter('my_location');
     if ($my_location) {
         $params['my_location'] = $my_location;
     }
     $user_uuid = $request->get_parameter('user_uuid');
     if ($user_uuid) {
         $params['user_uuid'] = $user_uuid;
     }
     # There's no need to validate the fields/lpc parameters as the 'geocaches'
     # method does this (it will raise a proper exception on invalid values).
     $results = OkapiServiceRunner::call('services/caches/geocaches', new OkapiInternalRequest($request->consumer, $request->token, $params));
     $result = $results[$cache_code];
     if ($result === null) {
         # Two errors messages (for OCDE). Makeshift solution for issue #350.
         $exists = Db::select_value("\n                select 1\n                from caches\n                where wp_oc='" . Db::escape_string($cache_code) . "'\n            ");
         if ($exists) {
             throw new InvalidParam('cache_code', "This cache is not accessible via OKAPI.");
         } else {
             throw new InvalidParam('cache_code', "This cache does not exist.");
         }
     }
     return Okapi::formatted_response($request, $result);
 }
 public static function call(OkapiRequest $request)
 {
     $result = array();
     $result['site_url'] = Settings::get('SITE_URL');
     $result['okapi_base_url'] = $result['site_url'] . "okapi/";
     $result['site_name'] = Okapi::get_normalized_site_name();
     $result['okapi_version_number'] = Okapi::$version_number;
     $result['okapi_revision'] = Okapi::$version_number;
     /* Important for backward-compatibility! */
     $result['okapi_git_revision'] = Okapi::$git_revision;
     return Okapi::formatted_response($request, $result);
 }
Beispiel #10
0
 public static function call(OkapiRequest $request)
 {
     require_once 'replicate_common.inc.php';
     $result = array();
     $result['changelog'] = array('min_since' => ReplicateCommon::get_min_since(), 'revision' => ReplicateCommon::get_revision());
     $dump = Cache::get("last_fulldump");
     if ($dump) {
         $result['latest_fulldump'] = array('revision' => $dump['revision'], 'generated_at' => $dump['meta']['generated_at'], 'size' => $dump['meta']['compressed_size'], 'size_uncompressed' => $dump['meta']['uncompressed_size']);
     } else {
         $result['latest_fulldump'] = null;
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #11
0
 public static function call(OkapiRequest $request)
 {
     $issue_id = $request->get_parameter('issue_id');
     if (!$issue_id) {
         throw new ParamMissing('issue_id');
     }
     if (!preg_match("/^[0-9]+\$/", $issue_id) || strlen($issue_id) > 6) {
         throw new InvalidParam('issue_id');
     }
     # In October 2013, Google Code feed at:
     # http://code.google.com/feeds/issues/p/opencaching-api/issues/$issue_id/comments/full
     # stopped working. We are forced to respond with a simple placeholder.
     $result = array('id' => $issue_id + 0, 'last_updated' => null, 'title' => null, 'url' => "https://code.google.com/p/opencaching-api/issues/detail?id=" . $issue_id, 'comment_count' => null);
     return Okapi::formatted_response($request, $result);
 }
Beispiel #12
0
 public static function call(OkapiRequest $request)
 {
     $username = $request->get_parameter('username');
     if (!$username) {
         throw new ParamMissing('username');
     }
     $fields = $request->get_parameter('fields');
     # There's no need to validate the fields parameter.
     $results = OkapiServiceRunner::call('services/users/by_usernames', new OkapiInternalRequest($request->consumer, $request->token, array('usernames' => $username, 'fields' => $fields)));
     $result = $results[$username];
     if ($result == null) {
         throw new InvalidParam('username', "There is no user by this username.");
     }
     return Okapi::formatted_response($request, $result);
 }
 public static function call(OkapiRequest $request)
 {
     $methodnames = OkapiServiceRunner::$all_names;
     sort($methodnames);
     $cache_key = "api_ref/method_index#" . md5(implode("#", $methodnames));
     $results = Cache::get($cache_key);
     if ($results == null) {
         $results = array();
         foreach ($methodnames as $methodname) {
             $info = OkapiServiceRunner::call('services/apiref/method', new OkapiInternalRequest(new OkapiInternalConsumer(), null, array('name' => $methodname)));
             $results[] = array('name' => $info['name'], 'brief_description' => $info['brief_description']);
         }
         Cache::set($cache_key, $results, 3600);
     }
     return Okapi::formatted_response($request, $results);
 }
 public static function call(OkapiRequest $request)
 {
     # Get current notes, and verify cache_code
     $cache_code = $request->get_parameter('cache_code');
     if ($cache_code == null) {
         throw new ParamMissing('cache_code');
     }
     $geocache = OkapiServiceRunner::call('services/caches/geocache', new OkapiInternalRequest($request->consumer, $request->token, array('cache_code' => $cache_code, 'fields' => 'my_notes|internal_id')));
     $current_value = $geocache['my_notes'];
     if ($current_value == null) {
         $current_value = "";
     }
     $cache_id = $geocache['internal_id'];
     # old_value
     $old_value = $request->get_parameter('old_value');
     if ($old_value === null) {
         $old_value = '';
     }
     # new_value (force "no HTML" policy).
     $new_value = $request->get_parameter('new_value');
     if ($new_value === null) {
         throw new ParamMissing('new_value');
     }
     # Force "no HTML" policy.
     $new_value = strip_tags($new_value);
     # Placeholders for returned values.
     $ret_saved_value = null;
     $ret_replaced = false;
     if (trim($current_value) == "" || self::str_equals($old_value, $current_value)) {
         /* REPLACE mode */
         $ret_replaced = true;
         if (trim($new_value) == "") {
             /* empty new value means delete */
             self::remove_notes($cache_id, $request->token->user_id);
             $ret_saved_value = null;
         } else {
             self::update_notes($cache_id, $request->token->user_id, $new_value);
             $ret_saved_value = $new_value;
         }
     } else {
         /* APPEND mode */
         $ret_saved_value = trim($current_value) . "\n\n" . trim($new_value);
         self::update_notes($cache_id, $request->token->user_id, $ret_saved_value);
     }
     $result = array('saved_value' => $ret_saved_value, 'replaced' => $ret_replaced);
     return Okapi::formatted_response($request, $result);
 }
Beispiel #15
0
 public static function call(OkapiRequest $request)
 {
     $cache_code = $request->get_parameter('cache_code');
     if (!$cache_code) {
         throw new ParamMissing('cache_code');
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         $fields = "uuid|date|user|type|comment";
     }
     $offset = $request->get_parameter('offset');
     if (!$offset) {
         $offset = "0";
     }
     if ((int) $offset != $offset || (int) $offset < 0) {
         throw new InvalidParam('offset', "Expecting non-negative integer.");
     }
     $limit = $request->get_parameter('limit');
     if (!$limit) {
         $limit = "none";
     }
     if ($limit == "none") {
         $limit = "999999999";
     }
     if ((int) $limit != $limit || (int) $limit < 0) {
         throw new InvalidParam('limit', "Expecting non-negative integer or 'none'.");
     }
     # Check if code exists and retrieve cache ID (this will throw
     # a proper exception on invalid code).
     $cache = OkapiServiceRunner::call('services/caches/geocache', new OkapiInternalRequest($request->consumer, null, array('cache_code' => $cache_code, 'fields' => 'internal_id')));
     # Cache exists. Getting the uuids of its logs.
     $log_uuids = Db::select_column("\n            select uuid\n            from cache_logs\n            where\n                cache_id = '" . Db::escape_string($cache['internal_id']) . "'\n                and " . (Settings::get('OC_BRANCH') == 'oc.pl' ? "deleted = 0" : "true") . "\n            order by date desc\n            limit {$offset}, {$limit}\n        ");
     # Getting the logs themselves. Formatting as an ordered list.
     $internal_request = new OkapiInternalRequest($request->consumer, $request->token, array('log_uuids' => implode("|", $log_uuids), 'fields' => $fields));
     $internal_request->skip_limits = true;
     $logs = OkapiServiceRunner::call('services/logs/entries', $internal_request);
     $results = array();
     foreach ($log_uuids as $log_uuid) {
         $results[] = $logs[$log_uuid];
     }
     /* Handle OCPL's "access logs" feature. */
     if (Settings::get('OC_BRANCH') == 'oc.pl' && Settings::get('OCPL_ENABLE_GEOCACHE_ACCESS_LOGS') && count($log_uuids) > 0) {
         require_once $GLOBALS['rootpath'] . 'okapi/lib/ocpl_access_logs.php';
         \okapi\OCPLAccessLogs::log_geocache_access($request, $cache['internal_id']);
     }
     return Okapi::formatted_response($request, $results);
 }
 public static function call(OkapiRequest $request)
 {
     $cache_code = $request->get_parameter('cache_code');
     if (!$cache_code) {
         throw new ParamMissing('cache_code');
     }
     if (strpos($cache_code, "|") !== false) {
         throw new InvalidParam('cache_code');
     }
     $langpref = $request->get_parameter('langpref');
     if (!$langpref) {
         $langpref = "en|" . Settings::get('SITELANG');
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         $fields = "code|name|location|type|status";
     }
     $log_fields = $request->get_parameter('log_fields');
     if (!$log_fields) {
         $log_fields = "uuid|date|user|type|comment";
     }
     $lpc = $request->get_parameter('lpc');
     if (!$lpc) {
         $lpc = 10;
     }
     $attribution_append = $request->get_parameter('attribution_append');
     if (!$attribution_append) {
         $attribution_append = 'full';
     }
     $params = array('cache_codes' => $cache_code, 'langpref' => $langpref, 'fields' => $fields, 'attribution_append' => $attribution_append, 'lpc' => $lpc, 'log_fields' => $log_fields);
     $my_location = $request->get_parameter('my_location');
     if ($my_location) {
         $params['my_location'] = $my_location;
     }
     $user_uuid = $request->get_parameter('user_uuid');
     if ($user_uuid) {
         $params['user_uuid'] = $user_uuid;
     }
     # There's no need to validate the fields/lpc parameters as the 'geocaches'
     # method does this (it will raise a proper exception on invalid values).
     $results = OkapiServiceRunner::call('services/caches/geocaches', new OkapiInternalRequest($request->consumer, $request->token, $params));
     $result = $results[$cache_code];
     if ($result === null) {
         throw new InvalidParam('cache_code', "This cache does not exist.");
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #17
0
 public static function call(OkapiRequest $request)
 {
     $log_uuid = $request->get_parameter('log_uuid');
     if (!$log_uuid) {
         throw new ParamMissing('log_uuid');
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         $fields = "date|user|type|comment";
     }
     $results = OkapiServiceRunner::call('services/logs/entries', new OkapiInternalRequest($request->consumer, $request->token, array('log_uuids' => $log_uuid, 'fields' => $fields)));
     $result = $results[$log_uuid];
     if ($result == null) {
         throw new InvalidParam('log_uuid', "This log entry does not exist.");
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #18
0
 public static function call(OkapiRequest $request)
 {
     $result = array();
     $result['site_url'] = Settings::get('SITE_URL');
     $result['okapi_base_url'] = Okapi::get_recommended_base_url();
     $result['okapi_base_urls'] = Okapi::get_allowed_base_urls();
     $result['site_name'] = Okapi::get_normalized_site_name();
     $result['okapi_version_number'] = Okapi::$version_number;
     $result['okapi_revision'] = Okapi::$version_number;
     /* Important for backward-compatibility! */
     $result['okapi_git_revision'] = Okapi::$git_revision;
     $result['registration_url'] = Settings::get('REGISTRATION_URL');
     $result['mobile_registration_url'] = Settings::get('MOBILE_REGISTRATION_URL');
     $result['image_max_upload_size'] = Settings::get('IMAGE_MAX_UPLOAD_SIZE');
     $result['image_rcmd_max_pixels'] = Settings::get('IMAGE_MAX_PIXEL_COUNT');
     return Okapi::formatted_response($request, $result);
 }
 public static function call(OkapiRequest $request)
 {
     require_once 'replicate_common.inc.php';
     $since = $request->get_parameter('since');
     if ($since === null) {
         throw new ParamMissing('since');
     }
     if ((int) $since != $since) {
         throw new InvalidParam('since');
     }
     # Let's check the $since parameter.
     if (!ReplicateCommon::check_since_param($since)) {
         throw new BadRequest("The 'since' parameter is too old. You must update your database more frequently.");
     }
     # Select a best chunk for the given $since, get the chunk from the database (or cache).
     list($from, $to) = ReplicateCommon::select_best_chunk($since);
     $clog_entries = ReplicateCommon::get_chunk($from, $to);
     $result = array('changelog' => &$clog_entries, 'revision' => $to + 0, 'more' => $to < ReplicateCommon::get_revision());
     return Okapi::formatted_response($request, $result);
 }
Beispiel #20
0
 public static function call(OkapiRequest $request)
 {
     $user_uuid = $request->get_parameter('user_uuid');
     if (!$user_uuid) {
         if ($request->token) {
             $tmp = OkapiServiceRunner::call('services/users/by_internal_id', new OkapiInternalRequest($request->consumer, null, array('internal_id' => $request->token->user_id, 'fields' => 'uuid')));
             $user_uuid = $tmp['uuid'];
         } else {
             throw new BadRequest("You must either: 1. supply the user_uuid argument, or " . "2. sign your request with an Access Token.");
         }
     }
     $fields = $request->get_parameter('fields');
     # There's no need to validate the fields parameter as the 'users'
     # method does this (it will raise a proper exception on invalid values).
     $results = OkapiServiceRunner::call('services/users/users', new OkapiInternalRequest($request->consumer, $request->token, array('user_uuids' => $user_uuid, 'fields' => $fields)));
     $result = $results[$user_uuid];
     if ($result == null) {
         throw new InvalidParam('user_uuid', "There is no user by this ID.");
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #21
0
 public static function call(OkapiRequest $request)
 {
     $usernames = $request->get_parameter('usernames');
     if (!$usernames) {
         throw new ParamMissing('usernames');
     }
     $usernames = explode("|", $usernames);
     if (count($usernames) > 500) {
         throw new InvalidParam('usernames', "Maximum allowed number of referenced users " . "is 500. You provided " . count($usernames) . " usernames.");
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         throw new ParamMissing('fields');
     }
     # There's no need to validate the fields parameter as the 'users'
     # method does this (it will raise a proper exception on invalid values).
     $rs = Db::query("\n            select username, uuid\n            from user\n            where username collate " . Settings::get('DB_CHARSET') . "_general_ci in ('" . implode("','", array_map('\\okapi\\Db::escape_string', $usernames)) . "')\n        ");
     $lower_username2useruuid = array();
     while ($row = Db::fetch_assoc($rs)) {
         $lower_username2useruuid[mb_strtolower($row['username'], 'utf-8')] = $row['uuid'];
     }
     Db::free_result($rs);
     # Retrieve data for the found user_uuids.
     if (count($lower_username2useruuid) > 0) {
         $id_results = OkapiServiceRunner::call('services/users/users', new OkapiInternalRequest($request->consumer, $request->token, array('user_uuids' => implode("|", array_values($lower_username2useruuid)), 'fields' => $fields)));
     } else {
         $id_results = array();
     }
     # Map user_uuids back to usernames. Also check which usernames were not found
     # and mark them with null.
     $results = array();
     foreach ($usernames as $username) {
         if (!isset($lower_username2useruuid[mb_strtolower($username, 'utf-8')])) {
             $results[$username] = null;
         } else {
             $results[$username] = $id_results[$lower_username2useruuid[mb_strtolower($username, 'utf-8')]];
         }
     }
     return Okapi::formatted_response($request, $results);
 }
Beispiel #22
0
 public static function call(OkapiRequest $request)
 {
     $user_uuid = $request->get_parameter('user_uuid');
     if (!$user_uuid) {
         throw new ParamMissing('user_uuid');
     }
     $limit = $request->get_parameter('limit');
     if (!$limit) {
         $limit = "20";
     }
     if (!is_numeric($limit)) {
         throw new InvalidParam('limit', "'{$limit}'");
     }
     $limit = intval($limit);
     if ($limit < 1 || $limit > 1000) {
         throw new InvalidParam('limit', "Has to be in range 1..1000.");
     }
     $offset = $request->get_parameter('offset');
     if (!$offset) {
         $offset = "0";
     }
     if (!is_numeric($offset)) {
         throw new InvalidParam('offset', "'{$offset}'");
     }
     $offset = intval($offset);
     if ($offset < 0) {
         throw new InvalidParam('offset', "'{$offset}'");
     }
     # Check if user exists and retrieve user's ID (this will throw
     # a proper exception on invalid UUID).
     $user = OkapiServiceRunner::call('services/users/user', new OkapiInternalRequest($request->consumer, null, array('user_uuid' => $user_uuid, 'fields' => 'internal_id')));
     # User exists. Retrieving logs.
     $rs = Db::query("\n            select cl.id, cl.uuid, cl.type, unix_timestamp(cl.date) as date, cl.text,\n                c.wp_oc as cache_code\n            from cache_logs cl, caches c\n            where\n                cl.user_id = '" . Db::escape_string($user['internal_id']) . "'\n                and " . (Settings::get('OC_BRANCH') == 'oc.pl' ? "cl.deleted = 0" : "true") . "\n                and c.status in (1,2,3)\n                and cl.cache_id = c.cache_id\n            order by cl.date desc\n            limit {$offset}, {$limit}\n        ");
     $results = array();
     while ($row = Db::fetch_assoc($rs)) {
         $results[] = array('uuid' => $row['uuid'], 'date' => date('c', $row['date']), 'cache_code' => $row['cache_code'], 'type' => Okapi::logtypeid2name($row['type']), 'comment' => $row['text']);
     }
     return Okapi::formatted_response($request, $results);
 }
Beispiel #23
0
 public static function call(OkapiRequest $request)
 {
     # "Cache control" parameters.
     $tmp = $request->get_parameter('min_store');
     if ($tmp === null) {
         $tmp = "300";
     }
     $min_store = intval($tmp);
     if ("{$min_store}" !== $tmp || $min_store < 0 || $min_store > 64800) {
         throw new InvalidParam('min_store', "Has to be in the 0..64800 range.");
     }
     $tmp = $request->get_parameter('ref_max_age');
     if ($tmp === null) {
         $tmp = "300";
     }
     if ($tmp == "nolimit") {
         $tmp = "9999999";
     }
     $ref_max_age = intval($tmp);
     if ("{$ref_max_age}" !== $tmp || $ref_max_age < 300) {
         throw new InvalidParam('ref_max_age', "Has to be >=300.");
     }
     # Search params.
     $search_assistant = new SearchAssistant($request);
     $search_assistant->prepare_common_search_params();
     $search_params = $search_assistant->get_search_params();
     $tables = array_merge(array('caches'), $search_params['extra_tables']);
     $where_conds = array_merge(array('caches.wp_oc is not null'), $search_params['where_conds']);
     if (isset($search_params['extra_joins']) && is_array($search_params['extra_joins'])) {
         $joins = $search_params['extra_joins'];
     } else {
         $joins = array();
     }
     unset($search_params);
     # Generate, or retrieve an existing set, and return the result.
     # All user-supplied data in $tables and $where_conds MUST be escaped!
     $result = self::get_set($tables, $joins, $where_conds, $min_store, $ref_max_age);
     return Okapi::formatted_response($request, $result);
 }
 public static function call(OkapiRequest $request)
 {
     # Read the parameters.
     $langpref = $request->get_parameter('langpref');
     if (!$langpref) {
         $langpref = "en";
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         $fields = "name";
     }
     $only_locally_used = $request->get_parameter('only_locally_used');
     if (!$only_locally_used) {
         $only_locally_used = "false";
     }
     $only_locally_used = $only_locally_used == "true";
     # Get the list of attributes and filter the A-codes based on the
     # parameters.
     require_once 'attr_helper.inc.php';
     $attrdict = AttrHelper::get_attrdict();
     $acodes = array();
     foreach ($attrdict as $acode => &$attr_ref) {
         if ($only_locally_used && $attr_ref['internal_id'] === null) {
             /* Skip. */
             continue;
         }
         $acodes[] = $acode;
     }
     # Retrieve the attribute objects and return the results.
     if (count($acodes) > 0) {
         $params = array('acodes' => implode("|", $acodes), 'langpref' => $langpref, 'fields' => $fields);
         $results = OkapiServiceRunner::call('services/attrs/attributes', new OkapiInternalRequest($request->consumer, $request->token, $params));
     } else {
         $results = new ArrayObject();
     }
     return Okapi::formatted_response($request, $results);
 }
 public static function call(OkapiRequest $request)
 {
     $internal_ids = $request->get_parameter('internal_ids');
     if (!$internal_ids) {
         throw new ParamMissing('internal_ids');
     }
     $internal_ids = explode("|", $internal_ids);
     if (count($internal_ids) > 500) {
         throw new InvalidParam('internal_ids', "Maximum allowed number of referenced users " . "is 500. You provided " . count($internal_ids) . " references.");
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         throw new ParamMissing('fields');
     }
     # There's no need to validate the fields parameter as the 'users'
     # method does this (it will raise a proper exception on invalid values).
     $rs = Db::query("\n            select user_id, uuid\n            from user\n            where user_id in ('" . implode("','", array_map('mysql_real_escape_string', $internal_ids)) . "')\n        ");
     $internalid2useruuid = array();
     while ($row = mysql_fetch_assoc($rs)) {
         $internalid2useruuid[$row['user_id']] = $row['uuid'];
     }
     mysql_free_result($rs);
     # Retrieve data on given user_uuids.
     $id_results = OkapiServiceRunner::call('services/users/users', new OkapiInternalRequest($request->consumer, $request->token, array('user_uuids' => implode("|", array_values($internalid2useruuid)), 'fields' => $fields)));
     # Map user_uuids to internal_ids. Also check which internal_ids were not found
     # and mark them with null.
     $results = array();
     foreach ($internal_ids as $internal_id) {
         if (!isset($internalid2useruuid[$internal_id])) {
             $results[$internal_id] = null;
         } else {
             $results[$internal_id] = $id_results[$internalid2useruuid[$internal_id]];
         }
     }
     return Okapi::formatted_response($request, $results);
 }
Beispiel #26
0
 public static function call(OkapiRequest $request)
 {
     # Retrieve the list of URLs to check.
     $tmp = $request->get_parameter('urls');
     if (!$tmp) {
         throw new ParamMissing('urls');
     }
     $urls = explode('|', $tmp);
     $as_dict = $request->get_parameter('as_dict');
     if (!$as_dict) {
         $as_dict = 'false';
     }
     if (!in_array($as_dict, array('true', 'false'))) {
         throw new InvalidParam('as_dict');
     }
     $as_dict = $as_dict == 'true';
     # Generate the lists of keys.
     $results = array();
     $urls_with = array('cache_code' => array(), 'internal_id' => array(), 'uuid' => array());
     foreach ($urls as &$url_ref) {
         $key = self::get_cache_key($url_ref);
         if ($key != null) {
             $urls_with[$key[0]][$url_ref] = $key[1];
         } else {
             $results[$url_ref] = null;
         }
     }
     # Include 'cache_code' references.
     foreach ($urls_with['cache_code'] as $url => $cache_code) {
         $results[$url] = $cache_code;
     }
     # Include 'internal_id' references.
     $internal_ids = array_values($urls_with['internal_id']);
     if (count($internal_ids) > 0) {
         $rs = Db::query("\n                select cache_id, wp_oc\n                from caches\n                where\n                    cache_id in ('" . implode("','", array_map('mysql_real_escape_string', $internal_ids)) . "')\n                    and status in (1,2,3)\n            ");
         $dict = array();
         while ($row = mysql_fetch_assoc($rs)) {
             $dict[$row['cache_id']] = $row['wp_oc'];
         }
         foreach ($urls_with['internal_id'] as $url => $internal_id) {
             if (isset($dict[$internal_id])) {
                 $results[$url] = $dict[$internal_id];
             } else {
                 $results[$url] = null;
             }
         }
     }
     # Include 'uuid' references.
     $uuids = array_values($urls_with['uuid']);
     if (count($uuids) > 0) {
         $rs = Db::query("\n                select uuid, wp_oc\n                from caches\n                where\n                    uuid in ('" . implode("','", array_map('mysql_real_escape_string', $uuids)) . "')\n                    and status in (1,2,3)\n            ");
         $dict = array();
         while ($row = mysql_fetch_assoc($rs)) {
             $dict[$row['uuid']] = $row['wp_oc'];
         }
         foreach ($urls_with['uuid'] as $url => $uuid) {
             if (isset($dict[$uuid])) {
                 $results[$url] = $dict[$uuid];
             } else {
                 $results[$url] = null;
             }
         }
     }
     # Format the results according to the 'as_dict' parameter.
     if ($as_dict) {
         return Okapi::formatted_response($request, $results);
     } else {
         $cache_codes = array();
         foreach ($results as $url => $cache_code) {
             if ($cache_code != null) {
                 $cache_codes[$cache_code] = true;
             }
         }
         $flattened = array('results' => array_keys($cache_codes));
         return Okapi::formatted_response($request, $flattened);
     }
 }
Beispiel #27
0
 public static function call(OkapiRequest $request)
 {
     # You may wonder, why there are no parameters like "bbox" or "center" in the
     # "search/all" method. This is *intentional* and should be kept this way.
     # Such parameters would fall in conflict with each other and - in result -
     # make the documentation very fuzzy. That's why they were intentionally
     # left out of the "search/all" method, and put in separate (individual) ones.
     # It's much easier to grasp their meaning this way.
     $tmp = $request->get_parameter('bbox');
     if (!$tmp) {
         throw new ParamMissing('bbox');
     }
     $parts = explode('|', $tmp);
     if (count($parts) != 4) {
         throw new InvalidParam('bbox', "Expecting 4 pipe-separated parts, got " . count($parts) . ".");
     }
     foreach ($parts as &$part_ref) {
         if (!preg_match("/^-?[0-9]+(\\.?[0-9]*)\$/", $part_ref)) {
             throw new InvalidParam('bbox', "'{$part_ref}' is not a valid float number.");
         }
         $part_ref = floatval($part_ref);
     }
     list($bbsouth, $bbwest, $bbnorth, $bbeast) = $parts;
     if ($bbnorth <= $bbsouth) {
         throw new InvalidParam('bbox', "Northern edge must be situated to the north of the southern edge.");
     }
     if ($bbeast == $bbwest) {
         throw new InvalidParam('bbox', "Eastern edge longitude is the same as the western one.");
     }
     if ($bbnorth > 90 || $bbnorth < -90 || $bbsouth > 90 || $bbsouth < -90) {
         throw new InvalidParam('bbox', "Latitudes have to be within -90..90 range.");
     }
     if ($bbeast > 180 || $bbeast < -180 || $bbwest > 180 || $bbwest < -180) {
         throw new InvalidParam('bbox', "Longitudes have to be within -180..180 range.");
     }
     # Construct SQL conditions for the specified bounding box.
     $search_assistant = new SearchAssistant($request);
     $search_assistant->prepare_common_search_params();
     $search_assistant->prepare_location_search_params();
     $where_conds = array();
     $lat = $search_assistant->get_latitude_expr();
     $lon = $search_assistant->get_longitude_expr();
     $where_conds[] = "(\n            {$lat} >= '" . Db::escape_string($bbsouth) . "'\n            and {$lat} < '" . Db::escape_string($bbnorth) . "'\n        )";
     if ($bbeast > $bbwest) {
         # Easy one.
         $where_conds[] = "(\n                {$lon} >= '" . Db::escape_string($bbwest) . "'\n                and {$lon} < '" . Db::escape_string($bbeast) . "'\n            )";
     } else {
         # We'll have to assume that this bbox goes through the 180-degree meridian.
         # For example, $bbwest = 179 and $bbeast = -179.
         $where_conds[] = "(\n                {$lon} >= '" . Db::escape_string($bbwest) . "'\n                or {$lon} < '" . Db::escape_string($bbeast) . "'\n            )";
     }
     #
     # In the method description, we promised to return caches ordered by the *rough*
     # distance from the center of the bounding box. We'll use ORDER BY with a simplified
     # distance formula and combine it with the LIMIT clause to get the best results.
     #
     $center_lat = ($bbsouth + $bbnorth) / 2.0;
     $center_lon = ($bbwest + $bbeast) / 2.0;
     $search_params = $search_assistant->get_search_params();
     $search_params['where_conds'] = array_merge($where_conds, $search_params['where_conds']);
     $search_params['order_by'][] = Okapi::get_distance_sql($center_lat, $center_lon, $search_assistant->get_latitude_expr(), $search_assistant->get_longitude_expr());
     # not replaced; added to the end!
     $search_assistant->set_search_params($search_params);
     $result = $search_assistant->get_common_search_result();
     return Okapi::formatted_response($request, $result);
 }
Beispiel #28
0
 public static function call(OkapiRequest $request)
 {
     # You may wonder, why there are no parameters like "bbox" or "center" in the
     # "search/all" method. This is *intentional* and should be kept this way.
     # Such parameters would fall in conflict with each other and - in result -
     # make the documentation very fuzzy. That's why they were intentionally
     # left out of the "search/all" method, and put in separate (individual) ones.
     # It's much easier to grasp their meaning this way.
     $tmp = $request->get_parameter('center');
     if (!$tmp) {
         throw new ParamMissing('center');
     }
     $parts = explode('|', $tmp);
     if (count($parts) != 2) {
         throw new InvalidParam('center', "Expecting 2 pipe-separated parts, got " . count($parts) . ".");
     }
     foreach ($parts as &$part_ref) {
         if (!preg_match("/^-?[0-9]+(\\.?[0-9]*)\$/", $part_ref)) {
             throw new InvalidParam('center', "'{$part_ref}' is not a valid float number.");
         }
         $part_ref = floatval($part_ref);
     }
     list($center_lat, $center_lon) = $parts;
     if ($center_lat > 90 || $center_lat < -90) {
         throw new InvalidParam('center', "Latitudes have to be within -90..90 range.");
     }
     if ($center_lon > 180 || $center_lon < -180) {
         throw new InvalidParam('center', "Longitudes have to be within -180..180 range.");
     }
     #
     # In the method description, we promised to return caches ordered by the *rough*
     # distance from the center point. We'll use ORDER BY with a simplified distance
     # formula and combine it with the LIMIT clause to get the best results.
     #
     $search_assistant = new SearchAssistant($request);
     $search_assistant->prepare_common_search_params();
     $search_assistant->prepare_location_search_params();
     $distance_formula = Okapi::get_distance_sql($center_lat, $center_lon, $search_assistant->get_latitude_expr(), $search_assistant->get_longitude_expr());
     # 'radius' parameter is optional. If not given, we'll have to calculate the
     # distance for every cache in the database.
     $where_conds = array();
     $radius = null;
     if ($tmp = $request->get_parameter('radius')) {
         if (!preg_match("/^-?[0-9]+(\\.?[0-9]*)\$/", $tmp)) {
             throw new InvalidParam('radius', "'{$tmp}' is not a valid float number.");
         }
         $radius = floatval($tmp);
         # is given in kilometers
         if ($radius <= 0) {
             throw new InvalidParam('radius', "Has to be a positive number.");
         }
         # Apply a latitude-range prefilter if it looks promising.
         # See https://github.com/opencaching/okapi/issues/363 for more info.
         $optimization_radius = 100;
         # in kilometers, optimized for Opencaching.de
         $km2degrees_upper_estimate_factor = 0.01;
         if ($radius <= $optimization_radius) {
             $radius_degrees = $radius * $km2degrees_upper_estimate_factor;
             $where_conds[] = "\n                    caches.latitude >= '" . Db::escape_string($center_lat - $radius_degrees) . "'\n                    and caches.latitude <= '" . Db::escape_string($center_lat + $radius_degrees) . "'\n                    ";
         }
         $radius *= 1000;
         # convert from kilometers to meters
         $where_conds[] = "{$distance_formula} <= '" . Db::escape_string($radius) . "'";
     }
     $search_params = $search_assistant->get_search_params();
     $search_params['where_conds'] = array_merge($where_conds, $search_params['where_conds']);
     $search_params['caches_indexhint'] = "use index (latitude)";
     $search_params['order_by'][] = $distance_formula;
     # not replaced; added to the end!
     $search_assistant->set_search_params($search_params);
     $result = $search_assistant->get_common_search_result();
     if ($radius == null) {
         # 'more' is meaningless in this case, we'll remove it.
         unset($result['more']);
     }
     return Okapi::formatted_response($request, $result);
 }
Beispiel #29
0
 public static function call(OkapiRequest $request)
 {
     $methodname = $request->get_parameter('name');
     if (!$methodname) {
         throw new ParamMissing('name');
     }
     if (!preg_match("#^services/[0-9a-z_/]*\$#", $methodname)) {
         throw new InvalidParam('name');
     }
     if (!OkapiServiceRunner::exists($methodname)) {
         throw new InvalidParam('name', "Method does not exist: '{$methodname}'.");
     }
     $options = OkapiServiceRunner::options($methodname);
     if (!isset($options['min_auth_level'])) {
         throw new Exception("Method {$methodname} is missing a required 'min_auth_level' option!");
     }
     $docs = simplexml_load_string(OkapiServiceRunner::docs($methodname));
     $exploded = explode("/", $methodname);
     $result = array('name' => $methodname, 'short_name' => end($exploded), 'ref_url' => Settings::get('SITE_URL') . "okapi/{$methodname}.html", 'auth_options' => array('min_auth_level' => $options['min_auth_level'], 'oauth_consumer' => $options['min_auth_level'] >= 2, 'oauth_token' => $options['min_auth_level'] >= 3));
     if (!$docs->brief) {
         throw new Exception("Missing <brief> element in the {$methodname}.xml file.");
     }
     if ($docs->brief != self::get_inner_xml($docs->brief)) {
         throw new Exception("The <brief> element may not contain HTML markup ({$methodname}.xml).");
     }
     if (strlen($docs->brief) > 80) {
         throw new Exception("The <brief> description may not be longer than 80 characters ({$methodname}.xml).");
     }
     if (strpos($docs->brief, "\n") !== false) {
         throw new Exception("The <brief> element may not contain new-lines ({$methodname}.xml).");
     }
     if (substr(trim($docs->brief), -1) == '.') {
         throw new Exception("The <brief> element should not end with a dot ({$methodname}.xml).");
     }
     $result['brief_description'] = self::get_inner_xml($docs->brief);
     if ($docs->{'issue-id'}) {
         $result['issue_id'] = (string) $docs->{'issue-id'};
     } else {
         $result['issue_id'] = null;
     }
     if (!$docs->desc) {
         throw new Exception("Missing <desc> element in the {$methodname}.xml file.");
     }
     $result['description'] = self::get_inner_xml($docs->desc);
     $result['arguments'] = array();
     foreach ($docs->req as $arg) {
         $result['arguments'][] = self::arg_desc($arg);
     }
     foreach ($docs->opt as $arg) {
         $result['arguments'][] = self::arg_desc($arg);
     }
     foreach ($docs->{'import-params'} as $import_desc) {
         $attrs = $import_desc->attributes();
         $referenced_methodname = $attrs['method'];
         $referenced_method_info = OkapiServiceRunner::call('services/apiref/method', new OkapiInternalRequest(new OkapiInternalConsumer(), null, array('name' => $referenced_methodname)));
         $include_list = isset($attrs['params']) ? explode("|", $attrs['params']) : null;
         $exclude_list = isset($attrs['except']) ? explode("|", $attrs['except']) : array();
         foreach ($referenced_method_info['arguments'] as $arg) {
             if ($arg['class'] == 'common-formatting') {
                 continue;
             }
             if ($include_list === null && count($exclude_list) == 0) {
                 $arg['description'] = "<i>Inherited from <a href='" . $referenced_method_info['ref_url'] . "#arg_" . $arg['name'] . "'>" . $referenced_method_info['name'] . "</a> method.</i>";
             } elseif (($include_list === null || in_array($arg['name'], $include_list)) && !in_array($arg['name'], $exclude_list)) {
                 $arg['description'] = "<i>Same as in the <a href='" . $referenced_method_info['ref_url'] . "#arg_" . $arg['name'] . "'>" . $referenced_method_info['name'] . "</a> method.</i>";
             } else {
                 continue;
             }
             $arg['class'] = 'inherited';
             $result['arguments'][] = $arg;
         }
     }
     if ($docs->{'common-format-params'}) {
         $result['arguments'][] = array('name' => 'format', 'is_required' => false, 'is_deprecated' => false, 'class' => 'common-formatting', 'description' => "<i>Standard <a href='" . Settings::get('SITE_URL') . "okapi/introduction.html#common-formatting'>common formatting</a> argument.</i>");
         $result['arguments'][] = array('name' => 'callback', 'is_required' => false, 'is_deprecated' => false, 'class' => 'common-formatting', 'description' => "<i>Standard <a href='" . Settings::get('SITE_URL') . "okapi/introduction.html#common-formatting'>common formatting</a> argument.</i>");
     }
     foreach ($result['arguments'] as &$arg_ref) {
         if ($arg_ref['is_deprecated']) {
             $arg_ref['class'] .= " deprecated";
         }
     }
     if (!$docs->returns) {
         throw new Exception("Missing <returns> element in the {$methodname}.xml file. " . "If your method does not return anything, you should document in nonetheless.");
     }
     $result['returns'] = self::get_inner_xml($docs->returns);
     return Okapi::formatted_response($request, $result);
 }
Beispiel #30
0
 public static function call(OkapiRequest $request)
 {
     $user_uuids = $request->get_parameter('user_uuids');
     if (!$user_uuids) {
         throw new ParamMissing('user_uuids');
     }
     $user_uuids = explode("|", $user_uuids);
     if (count($user_uuids) > 500) {
         throw new InvalidParam('user_uuids', "Maximum allowed number of referenced users " . "is 500. You provided " . count($user_uuids) . " user IDs.");
     }
     $fields = $request->get_parameter('fields');
     if (!$fields) {
         throw new ParamMissing('fields');
     }
     $fields = explode("|", $fields);
     foreach ($fields as $field) {
         if (!in_array($field, self::$valid_field_names)) {
             throw new InvalidParam('fields', "'{$field}' is not a valid field code.");
         }
     }
     $rs = Db::query("\n            select user_id, uuid, username, admin, latitude, longitude, date_created\n            from user\n            where uuid in ('" . implode("','", array_map('\\okapi\\Db::escape_string', $user_uuids)) . "')\n        ");
     $results = array();
     $id2uuid = array();
     $uuid2id = array();
     while ($row = Db::fetch_assoc($rs)) {
         $id2uuid[$row['user_id']] = $row['uuid'];
         $uuid2id[$row['uuid']] = $row['user_id'];
         $entry = array();
         foreach ($fields as $field) {
             switch ($field) {
                 case 'uuid':
                     $entry['uuid'] = $row['uuid'];
                     break;
                 case 'username':
                     $entry['username'] = $row['username'];
                     break;
                 case 'profile_url':
                     $entry['profile_url'] = Settings::get('SITE_URL') . "viewprofile.php?userid=" . $row['user_id'];
                     break;
                 case 'is_admin':
                     if (!$request->token) {
                         $entry['is_admin'] = null;
                     } elseif ($request->token->user_id != $row['user_id']) {
                         $entry['is_admin'] = null;
                     } else {
                         $entry['is_admin'] = $row['admin'] ? true : false;
                     }
                     break;
                 case 'internal_id':
                     $entry['internal_id'] = $row['user_id'];
                     break;
                 case 'date_registered':
                     $entry['date_registered'] = date("Y-m-d", strtotime($row['date_created']));
                 case 'caches_found':
                     /* handled separately */
                     break;
                 case 'caches_notfound':
                     /* handled separately */
                     break;
                 case 'caches_hidden':
                     /* handled separately */
                     break;
                 case 'rcmds_given':
                     /* handled separately */
                     break;
                 case 'home_location':
                     if (!$request->token) {
                         $entry['home_location'] = null;
                     } elseif ($request->token->user_id != $row['user_id']) {
                         $entry['home_location'] = null;
                     } elseif (!$row['latitude'] && !$row['longitude']) {
                         # OCPL sets NULL/NULL for unknown location, OCDE sets 0/0.
                         # It is safe to return null also for OCPL 0/0, as this value
                         # does not make sense.
                         $entry['home_location'] = null;
                     } else {
                         $entry['home_location'] = round($row['latitude'], 6) . "|" . round($row['longitude'], 6);
                     }
                     break;
                 default:
                     throw new Exception("Missing field case: " . $field);
             }
         }
         $results[$row['uuid']] = $entry;
     }
     Db::free_result($rs);
     # caches_found, caches_notfound, caches_hidden
     if (in_array('caches_found', $fields) || in_array('caches_notfound', $fields) || in_array('caches_hidden', $fields) || in_array('rcmds_given', $fields)) {
         # We will load all these stats together. Then we may remove these which
         # the user doesn't need.
         $extras = array();
         if (Settings::get('OC_BRANCH') == 'oc.pl') {
             # OCPL stores user stats in 'user' table.
             $rs = Db::query("\n                    select user_id, founds_count, notfounds_count, hidden_count\n                    from user\n                    where user_id in ('" . implode("','", array_map('\\okapi\\Db::escape_string', array_keys($id2uuid))) . "')\n                ");
         } else {
             # OCDE stores user stats in 'stat_user' table.
             $rs = Db::query("\n                    select\n                        u.user_id,\n                        ifnull(su.found, 0) as founds_count,\n                        ifnull(su.notfound, 0) as notfounds_count,\n                        ifnull(su.hidden, 0) as hidden_count\n                    from\n                        user u\n                        left join stat_user su\n                            on su.user_id = u.user_id\n                    where u.user_id in ('" . implode("','", array_map('\\okapi\\Db::escape_string', array_keys($id2uuid))) . "')\n                ");
         }
         while ($row = Db::fetch_assoc($rs)) {
             $extras[$row['user_id']] = array();
             $extra_ref =& $extras[$row['user_id']];
             $extra_ref['caches_found'] = 0 + $row['founds_count'];
             $extra_ref['caches_notfound'] = 0 + $row['notfounds_count'];
             $extra_ref['caches_hidden'] = 0 + $row['hidden_count'];
         }
         Db::free_result($rs);
         if (in_array('rcmds_given', $fields)) {
             $rs = Db::query("\n                    select user_id, count(*) as rcmds_given\n                    from cache_rating\n                    where user_id in ('" . implode("','", array_map('\\okapi\\Db::escape_string', array_keys($id2uuid))) . "')\n                    group by user_id\n                ");
             $rcmds_counts = array();
             while ($row = Db::fetch_assoc($rs)) {
                 $rcmds_counts[$row['user_id']] = $row['rcmds_given'];
             }
             foreach ($extras as $user_id => &$extra_ref) {
                 $extra_ref['rcmds_given'] = isset($rcmds_counts[$user_id]) ? 0 + $rcmds_counts[$user_id] : 0;
             }
         }
         # "Apply" only those fields which the consumer wanted.
         foreach (array('caches_found', 'caches_notfound', 'caches_hidden', 'rcmds_given') as $field) {
             if (!in_array($field, $fields)) {
                 continue;
             }
             foreach ($results as $uuid => &$result_ref) {
                 $result_ref[$field] = $extras[$uuid2id[$uuid]][$field];
             }
         }
     }
     # Check which user IDs were not found and mark them with null.
     foreach ($user_uuids as $user_uuid) {
         if (!isset($results[$user_uuid])) {
             $results[$user_uuid] = null;
         }
     }
     return Okapi::formatted_response($request, $results);
 }