Test Failed
Push — master ( b0dc6a...0556e5 )
by Stefan
06:36
created

ExternalEduroamDBData::countAllServiceProviders()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 16
c 1
b 0
f 1
dl 0
loc 21
rs 9.1111
cc 6
nc 4
nop 0
1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
23
/**
24
 * This file contains the ExternalEduroamDBData class. It contains methods for
25
 * querying the external database.
26
 *
27
 * @author Stefan Winter <[email protected]>
28
 *
29
 * @package Developer
30
 *
31
 */
32
33
namespace core;
34
35
use \Exception;
36
37
/**
38
 * This class interacts with the external DB to fetch operational data for
39
 * read-only purposes.
40
 * 
41
 * @author Stefan Winter <[email protected]>
42
 *
43
 * @license see LICENSE file in root directory
44
 *
45
 * @package Developer
46
 */
47
class ExternalEduroamDBData extends common\Entity implements ExternalLinkInterface
48
{
49
50
    /**
51
     * List of all service providers. Fetched only once by allServiceProviders()
52
     * and then stored in this property for efficiency
53
     * 
54
     * @var array
55
     */
56
    private $SPList = [];
57
    
58
    /**
59
     * total number of hotspots, cached here for efficiency
60
     * 
61
     * @var int
62
     */
63
    private $counter = -1;
64
65
    /**
66
     * our handle to the DB
67
     * 
68
     * @var DBConnection
69
     */
70
    private $db;
71
72
    /**
73
     * constructor, gives us access to the DB handle we need for queries
74
     */
75
    public function __construct()
76
    {
77
        parent::__construct();
78
        $connHandle = DBConnection::handle("EXTERNAL");
79
        if (!$connHandle instanceof DBConnection) {
80
            throw new Exception("Frontend DB is never an array, always a single DB object.");
81
        }
82
        $this->db = $connHandle;
83
        $this->db->exec("SET NAMES 'latin1'");
84
    }
85
86
    /**
87
     * eduroam DB delivers a string with all name variants mangled in one. Pry
88
     * it apart.
89
     * 
90
     * @param string $nameRaw the string with all name variants coerced into one
91
     * @return array language/name pair
92
     * @throws Exception
93
     */
94
    private function splitNames($nameRaw)
95
    {
96
        $variants = explode('#', $nameRaw);
97
        $submatches = [];
98
        $returnArray = [];
99
        foreach ($variants as $oneVariant) {
100
            if ($oneVariant == NULL) {
101
                continue;
102
            }
103
            if (!preg_match('/^(..):\ (.*)/', $oneVariant, $submatches) || !isset($submatches[2])) {
104
                $this->loggerInstance->debug(2, "[$nameRaw] We expect 'xx: bla but found '$oneVariant'.");
105
                continue;
106
            }
107
            $returnArray[$submatches[1]] = $submatches[2];
108
        }
109
        return $returnArray;
110
    }
111
112
    /**
113
     * retrieves the list of all service providers from the eduroam database
114
     * 
115
     * @return array list of providers
116
     */
117
    public function listAllServiceProviders()
118
    {
119
        if (count($this->SPList) == 0) {
120
            $query = $this->db->exec("SELECT country, inst_name, sp_location FROM view_active_SP_location_eduroamdb");
121
            while ($iterator = mysqli_fetch_object(/** @scrutinizer ignore-type */ $query)) {
122
                $this->SPList[] = ["country" => $iterator->country, "instnames" => $this->splitNames($iterator->inst_name), "locnames" => $this->splitNames($iterator->sp_location)];
123
            }
124
        }
125
        return $this->SPList;
126
    }
127
    
128
    public function countAllServiceProviders()
129
    {
130
                if ($this->counter > -1) {
131
            return $this->counter;
132
        }
133
134
        $cachedNumber = @file_get_contents(ROOT . "/var/tmp/cachedSPNumber.serialised");
135
        if ($cachedNumber !== FALSE) {
136
            $numberData = unserialize($cachedNumber);
137
            $now = new \DateTime();
138
            $cacheDate = $numberData["timestamp"]; // this is a DateTime object
139
            $diff = $now->diff($cacheDate);
140
            if ($diff->y == 0 && $diff->m == 0 && $diff->d == 0) {
141
                $this->counter = $numberData["number"];
142
                return $this->counter;
143
            }
144
        } else { // data in cache is too old or doesn't exist. We really need to ask the database
145
            $list = $this->listAllServiceProviders();
146
            $this->counter = count($list);
147
            file_put_contents(ROOT . "/var/tmp/cachedSPNumber.serialised", serialize(["number" => $this->counter, "timestamp" => new \DateTime()]));
148
            return $this->counter;
149
        }
150
    }
151
152
    public const TYPE_IDPSP = "1";
153
    public const TYPE_SP = "2";
154
    public const TYPE_IDP = "3";
155
    private const TYPE_MAPPING = [
156
        IdP::TYPE_IDP => ExternalEduroamDBData::TYPE_IDP,
157
        IdP::TYPE_IDPSP => ExternalEduroamDBData::TYPE_IDPSP,
158
        IdP::TYPE_SP => ExternalEduroamDBData::TYPE_SP,
159
    ];
160
161
    /**
162
     * retrieves entity information from the eduroam database. Choose whether to get all entities with an SP role, an IdP role, or only those with both roles
163
     * 
164
     * @param string      $tld  the top-level domain from which to fetch the entities
165
     * @param string|NULL $type type of entity to retrieve
166
     * @return array list of entities
167
     */
168
    public function listExternalEntities($tld, $type)
169
    {
170
        if ($type === NULL) {
171
            $eduroamDbType = NULL;
172
        } else {
173
            $eduroamDbType = self::TYPE_MAPPING[$type]; // anything
174
        }
175
        $returnarray = [];
176
        $query = "SELECT id_institution AS id, country, inst_realm as realmlist, name AS collapsed_name, contact AS collapsed_contact, type FROM view_active_institution WHERE country = ?";
177
        if ($eduroamDbType !== NULL) {
178
            $query .= " AND ( type = '" . ExternalEduroamDBData::TYPE_IDPSP . "' OR type = '" . $eduroamDbType . "')";
179
        }
180
        $externals = $this->db->exec($query, "s", $tld);
181
        // was a SELECT query, so a resource and not a boolean
182
        while ($externalQuery = mysqli_fetch_object(/** @scrutinizer ignore-type */ $externals)) {
183
            $names = $this->splitNames($externalQuery->collapsed_name);
184
            $thelanguage = $names[$this->languageInstance->getLang()] ?? $names["en"] ?? array_shift($names);
185
            $contacts = explode('#', $externalQuery->collapsed_contact);
186
            $mailnames = "";
187
            foreach ($contacts as $contact) {
188
                $matches = [];
189
                preg_match("/^n: (.*), e: (.*), p: .*$/", $contact, $matches);
190
                if ($matches[2] != "") {
191
                    if ($mailnames != "") {
192
                        $mailnames .= ", ";
193
                    }
194
                    // extracting real names is nice, but the <> notation
195
                    // really gets screwed up on POSTs and HTML safety
196
                    // so better not do this; use only mail addresses
197
                    $mailnames .= $matches[2];
198
                }
199
            }
200
            $convertedType = array_search($externalQuery->type, self::TYPE_MAPPING);
201
            $returnarray[] = ["ID" => $externalQuery->id, "name" => $thelanguage, "contactlist" => $mailnames, "country" => $externalQuery->country, "realmlist" => $externalQuery->realmlist, "type" => $convertedType];
202
        }
203
        usort($returnarray, array($this, "usortInstitution"));
204
        return $returnarray;
205
    }
206
207
    /**
208
     * helper function to sort institutions by their name
209
     * 
210
     * @param array $a an array with institution a's information
211
     * @param array $b an array with institution b's information
212
     * @return int the comparison result
213
     */
214
    private function usortInstitution($a, $b)
215
    {
216
        return strcasecmp($a["name"], $b["name"]);
217
    }
218
}