Passed
Push — master ( 54cc30...cc7767 )
by Maja
08:24
created

mobileconfigSuperclass   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 521
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 521
rs 6.96
c 0
b 0
f 0
wmc 53

17 Methods

Rating   Name   Duplication   Size   Complexity  
A expiryBlock() 0 7 2
A writeDeviceInfo() 0 21 3
A proxySettings() 0 20 2
A caBlob() 0 29 1
B eapBlock() 0 48 6
A removenetworkBlock() 0 29 1
A passPointBlock() 0 37 4
A generalPayload() 0 22 3
A __construct() 0 5 1
A clientP12Block() 0 32 2
A allCA() 0 8 2
B networkBlock() 0 69 7
A listCAUuids() 0 6 2
B writeInstaller() 0 81 5
A massageName() 0 2 1
A consentBlock() 0 11 4
B allNetworkBlocks() 0 21 7

How to fix   Complexity   

Complex Class

Complex classes like mobileconfigSuperclass often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use mobileconfigSuperclass, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
/**
13
 * This file contains the installer for iOS devices and Apple 10.7 Lion
14
 *
15
 *
16
 * @author Stefan Winter <[email protected]>
17
 * @package Developer
18
 */
19
20
namespace devices\apple_mobileconfig;
21
22
use \Exception;
23
24
/**
25
 * This is the main implementation class of the module
26
 *
27
 * The class should only define one public method: writeInstaller.
28
 *
29
 * All other methods and properties should be private. This example sets zipInstaller method to protected, so that it can be seen in the documentation.
30
 *
31
 * @package Developer
32
 */
33
abstract class mobileconfigSuperclass extends \core\DeviceConfig {
34
35
    private $instName;
36
    private $profileName;
37
    private $massagedInst;
38
    private $massagedProfile;
39
    private $massagedCountry;
40
    private $massagedConsortium;
41
    private $lang;
42
    static private $iPhonePayloadPrefix = "org.1x-config";
43
    private $clientCertUUID;
44
45
    public function __construct() {
46
        parent::__construct();
47
        // that's what all variants support. Sub-classes can change it.
48
        $this->setSupportedEapMethods([\core\common\EAP::EAPTYPE_PEAP_MSCHAP2, \core\common\EAP::EAPTYPE_TTLS_PAP, \core\common\EAP::EAPTYPE_TTLS_MSCHAP2, \core\common\EAP::EAPTYPE_SILVERBULLET]);
49
        $this->specialities['internal:verify_userinput_suffix'] = _("It is not possible to actively verify the user input for suffix match; but if there is no 'Terms of Use' configured, the installer will display a corresponding hint to the user instead.");
50
    }
51
52
    private function massageName($input) {
53
        return htmlspecialchars(strtolower(iconv("UTF-8", "US-ASCII//TRANSLIT", preg_replace(['/ /', '/\//'], '_', $input))), ENT_XML1, 'UTF-8');
54
    }
55
56
    private function generalPayload() {
57
        $tagline = sprintf(_("Network configuration profile '%s' of '%s' - provided by %s"), htmlspecialchars($this->profileName, ENT_XML1, 'UTF-8'), htmlspecialchars($this->instName, ENT_XML1, 'UTF-8'), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
58
59
        $eapType = $this->selectedEap;
60
        // simpler message for silverbullet
61
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
62
            $tagline = sprintf(_("%s configuration for IdP '%s' - provided by %s"), \core\ProfileSilverbullet::PRODUCTNAME, htmlspecialchars($this->instName, ENT_XML1, 'UTF-8'), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
63
        }
64
65
        return "
66
      <key>PayloadDescription</key>
67
         <string>$tagline</string>
68
      <key>PayloadDisplayName</key>
69
         <string>" . CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'] . "</string>
70
      <key>PayloadIdentifier</key>
71
         <string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.$this->lang</string>
72
      <key>PayloadOrganization</key>
73
         <string>" . htmlspecialchars(iconv("UTF-8", "UTF-8//IGNORE", $this->attributes['general:instname'][0]), ENT_XML1, 'UTF-8') . ( $this->attributes['internal:profile_count'][0] > 1 ? " (" . htmlspecialchars(iconv("UTF-8", "UTF-8//IGNORE", $this->attributes['profile:name'][0]), ENT_XML1, 'UTF-8') . ")" : "") . "</string>
74
      <key>PayloadType</key>
75
         <string>Configuration</string>
76
      <key>PayloadUUID</key>
77
         <string>" . \core\common\Entity::uuid('', self::$iPhonePayloadPrefix . $this->massagedConsortium . $this->massagedCountry . $this->massagedInst . $this->massagedProfile) . "</string>
78
      <key>PayloadVersion</key>
79
         <integer>1</integer>";
80
    }
81
82
    const FILE_START = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
83
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"
84
\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
85
<plist version=\"1.0\">
86
<dict>";
87
    const FILE_END = "</dict></plist>";
88
    const BUFFER_CONSENT_PRE = "
89
      <key>ConsentText</key>
90
         <dict>
91
            <key>default</key>
92
               <string>";
93
    const BUFFER_CONSENT_POST = "</string>
94
         </dict>
95
         ";
96
97
    protected function consentBlock() {
98
        if (isset($this->attributes['support:info_file'])) {
99
            return mobileconfigSuperclass::BUFFER_CONSENT_PRE . htmlspecialchars(iconv("UTF-8", "UTF-8//TRANSLIT", $this->attributes['support:info_file'][0]), ENT_XML1, 'UTF-8') . mobileconfigSuperclass::BUFFER_CONSENT_POST;
100
        }
101
        if (isset($this->attributes['internal:verify_userinput_suffix'])) {
102
            if (isset($this->attributes['internal:realm'])) {
103
                return mobileconfigSuperclass::BUFFER_CONSENT_PRE . sprintf(_("Important Notice: your username must end with @%s!"), $this->attributes['internal:realm'][0]) . mobileconfigSuperclass::BUFFER_CONSENT_POST;
104
            }
105
            return mobileconfigSuperclass::BUFFER_CONSENT_PRE . _("Important Notice: your username MUST be in the form of xxx@yyy where the yyy is a common suffix identifying your Identity Provider. Please find out what to use there and enter the username in the correct format.") . mobileconfigSuperclass::BUFFER_CONSENT_POST;
106
        }
107
        return "";
108
    }
109
110
    /**
111
     * prepare a zip archive containing files and settings which normally would be used inside the module to produce an installer
112
     *
113
     */
114
    public function writeInstaller() {
115
        /** run innitial setup
116
          this will:
117
          - create the temporary directory and save its path as $this->FPATH
118
          - process the CA certificates and store results in $this->attributes['internal:CAs'][0]
119
          $this->attributes['internal:CAs'][0] is an array of processed CA certificates
120
          a processed certifincate is an array
121
          'pem' points to pem feromat certificate
122
          'der' points to der format certificate
123
          'md5' points to md5 fingerprint
124
          'sha1' points to sha1 fingerprint
125
          'name' points to the certificate subject
126
          'root' can be 1 for self-signed certificate or 0 otherwise
127
128
          - save the info_file (if exists) and put the name in $this->attributes['internal:info_file_name'][0]
129
         */
130
        $dom = textdomain(NULL);
131
        textdomain("devices");
132
133
        $this->loggerInstance->debug(4, "mobileconfig Module Installer start\n");
134
135
        // remove spaces and slashes (filename!), make sure it's simple ASCII only, then lowercase it
136
        // also escape htmlspecialchars
137
        // not all names and profiles have a name, so be prepared
138
139
        $this->loggerInstance->debug(5, "List of available attributes: " . var_export($this->attributes, TRUE));
140
141
        $this->instName = $this->attributes['general:instname'][0] ?? sprintf(_("Unnamed %s"), $this->nomenclature_inst);
142
        $this->profileName = $this->attributes['profile:name'][0] ?? _("Unnamed Profile");
143
144
        $this->massagedInst = $this->massageName($this->instName);
145
        $this->massagedProfile = $this->massageName($this->profileName);
146
        $this->massagedCountry = $this->massageName($this->attributes['internal:country'][0]);
147
        $this->massagedConsortium = $this->massageName(CONFIG_CONFASSISTANT['CONSORTIUM']['name']);
148
        $this->lang = preg_replace('/\..+/', '', setlocale(LC_ALL, "0"));
149
150
        $eapType = $this->selectedEap;
151
152
        $outputXml = self::FILE_START;
153
        $outputXml .= "<key>PayloadContent</key>
154
         <array>";
155
156
        // if we are in silverbullet, we will need a whole own block for the client credential
157
        // and also for the profile expiry
158
159
        $this->clientCertUUID = NULL;
160
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
161
            $blockinfo = $this->clientP12Block();
162
            $outputXml .= $blockinfo['block'];
163
            $this->clientCertUUID = $blockinfo['UUID'];
164
        }
165
166
        $outputXml .= $this->allCA();
167
168
        $outputXml .= $this->allNetworkBlocks();
169
170
        $outputXml .= "</array>";
171
        $outputXml .= $this->generalPayload();
172
        $outputXml .= $this->consentBlock();
173
174
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
175
            $outputXml .= $this->expiryBlock();
176
        }
177
        $outputXml .= self::FILE_END;
178
179
        file_put_contents('installer_profile', $outputXml);
180
181
        textdomain($dom);
182
183
        $fileName = $this->installerBasename . '.mobileconfig';
184
185
        if (!$this->sign) {
186
            rename("installer_profile", $fileName);
187
            return $fileName;
188
        }
189
        // still here? Then we are signing.
190
        $signing = system($this->sign . " installer_profile '$fileName' > /dev/null");
191
        if ($signing === FALSE) {
192
            $this->loggerInstance->debug(2, "Signing the mobileconfig installer $fileName FAILED!\n");
193
        }
194
        return $fileName;
195
    }
196
197
    public function writeDeviceInfo() {
198
        $ssidCount = count($this->attributes['internal:SSID']);
199
        $certCount = count($this->attributes['internal:CAs'][0]);
200
        $out = "<p>" . _("For best results, please use the built-in browser (Safari) to open the configuration file.") . "</p>";
201
        $out .= "<p>";
202
        $out .= _("The profile will install itself after you click (or tap) the button. You will be asked for confirmation/input at several points:");
203
        $out .= "<ul>";
204
        $out .= "<li>" . _("to install the profile") . "</li>";
205
        $out .= "<li>" . ngettext("to accept the server certificate authority", "to accept the server certificate authorities", $certCount);
206
        if ($certCount > 1) {
207
            $out .= " " . sprintf(_("(%d times)"), $certCount);
208
        }
209
        $out .= "</li>";
210
        $out .= "<li>" . sprintf(_("to enter the username and password of your %s"), $this->nomenclature_inst);
211
        if ($ssidCount > 1) {
212
            $out .= " " . sprintf(_("(%d times each, because %s is installed for %d SSIDs)"), $ssidCount, CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], $ssidCount);
213
        }
214
        $out .= "</li>";
215
        $out .= "</ul>";
216
        $out .= "</p>";
217
        return $out;
218
    }
219
220
    private function listCAUuids() {
221
        $retval = [];
222
        foreach ($this->attributes['internal:CAs'][0] as $ca) {
223
            $retval[] = $ca['uuid'];
224
        }
225
        return $retval;
226
    }
227
228
    private function passPointBlock($consortiumOi) {
229
        $retval = "
230
               <key>IsHotspot</key>
231
               <true/>
232
               <key>ServiceProviderRoamingEnabled</key>
233
               <true/>
234
               <key>DisplayedOperatorName</key>
235
               <string>" . CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'] . " via Passpoint</string>";
236
        // if we don't know the realm, omit the entire DomainName key
237
        if (isset($this->attributes['internal:realm'])) {
238
            $retval .= "<key>DomainName</key>
239
               <string>";
240
            $retval .= $this->attributes['internal:realm'][0];
241
            $retval .= "</string>
242
                ";
243
        }
244
        $retval .= "                <key>RoamingConsortiumOIs</key>
245
                <array>";
246
        foreach ($consortiumOi as $oiValue) {
247
            $retval .= "<string>$oiValue</string>";
248
        }
249
        $retval .= "</array>";
250
        // this is an undocmented value found on the net. Does it do something useful?
251
        $retval .= "<key>_UsingHotspot20</key>
252
                <true/>
253
                ";
254
        // do we need to set NAIRealmName ? In Rel 1, probably yes, in Rel 2, 
255
        // no because ConsortiumOI is enough.
256
        // but which release is OS X doing? And what should we fill in, given
257
        // that we have thousands of realms? Try just eduroam.org
258
        if (CONFIG_CONFASSISTANT['CONSORTIUM']['name'] == "eduroam") {
259
            $retval .= "<key>NAIRealmNames</key>
260
                <array>
261
                    <string>eduroam.org</string>
262
                </array>";
263
        }
264
        return $retval;
265
    }
266
267
    private $serial;
268
    private $removeSerial;
269
    private $caSerial;
270
271
    private function eapBlock($eapType) {
272
        $realm = $this->determineOuterIdString();
273
        $retval = "<key>EAPClientConfiguration</key>
274
                  <dict>
275
                      <key>AcceptEAPTypes</key>
276
                         <array>
277
                            <integer>" . $eapType['OUTER'] . "</integer>
278
                         </array>
279
                      <key>EAPFASTProvisionPAC</key>
280
                            <true />
281
                      <key>EAPFASTUsePAC</key>
282
                            <true />
283
                      <key>EAPFastProvisionPACAnonymously</key>
284
                            <false />
285
                      <key>OneTimeUserPassword</key>
286
                            <false />
287
";
288
        if ($realm !== NULL) {
289
            $retval .= "<key>OuterIdentity</key>
290
                                    <string>" . htmlspecialchars($realm, ENT_XML1, 'UTF-8') . "</string>
291
";
292
        }
293
        $retval .= "<key>PayloadCertificateAnchorUUID</key>
294
                         <array>";
295
        foreach ($this->listCAUuids() as $uuid) {
296
            $retval .= "
297
<string>$uuid</string>";
298
        }
299
        $retval .= "
300
                         </array>
301
                      <key>TLSAllowTrustExceptions</key>
302
                         <false />
303
                      <key>TLSTrustedServerNames</key>
304
                         <array>";
305
        foreach ($this->attributes['eap:server_name'] as $commonName) {
306
            $retval .= "
307
<string>$commonName</string>";
308
        }
309
        $retval .= "
310
                         </array>";
311
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
312
            $retval .= "<key>UserName</key><string>" . $this->clientCert["certObject"]->username . "</string>";
313
        }
314
        $retval .= "
315
                      <key>TTLSInnerAuthentication</key>
316
                         <string>" . ($eapType['INNER'] == \core\common\EAP::NONE ? "PAP" : "MSCHAPv2") . "</string>
317
                   </dict>";
318
        return $retval;
319
    }
320
321
    protected function proxySettings() {
322
        $buffer = "<key>ProxyType</key>";
323
        if (isset($this->attributes['media:force_proxy'])) {
324
            // find the port delimiter. In case of IPv6, there are multiple ':' 
325
            // characters, so we have to find the LAST one
326
            $serverAndPort = explode(':', strrev($this->attributes['media:force_proxy'][0]), 2);
327
            // characters are still reversed, invert on use!
328
            $buffer .= "<string>Manual</string>
329
                  <key>ProxyServer</key>
330
                  <string>" . strrev($serverAndPort[1]) . "</string>
331
                  <key>ProxyServerPort</key>
332
                  <integer>" . strrev($serverAndPort[0]) . "</integer>
333
                  <key>ProxyPACFallbackAllowed</key>
334
                  <false/>";
335
        } else {
336
            $buffer .= "<string>Auto</string>
337
                  <key>ProxyPACFallbackAllowed</key>
338
                  <true/>";
339
        }
340
        return $buffer;
341
    }
342
343
    private function networkBlock($blocktype, $toBeConfigured) {
344
        $eapType = $this->selectedEap;
345
        switch ($blocktype) {
346
            case mobileconfigSuperclass::NETWORK_BLOCK_TYPE_SSID:
347
                $escapedSSID = htmlspecialchars($toBeConfigured, ENT_XML1, 'UTF-8');
348
                $payloadIdentifier = "wifi." . $this->serial;
349
                $payloadShortName = sprintf(_("SSID %s"), $escapedSSID);
350
                $payloadName = sprintf(_("%s configuration for network name %s"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], $escapedSSID);
351
                $encryptionTypeString = "WPA";
352
                $setupModesString = "";
353
                $wifiNetworkIdentification = "<key>SSID_STR</key>
354
                  <string>$escapedSSID</string>";
355
                break;
356
            case mobileconfigSuperclass::NETWORK_BLOCK_TYPE_WIRED:
357
                $payloadIdentifier = "firstactiveethernet";
358
                $payloadShortName = _("Wired Network");
359
                $payloadName = sprintf(_("%s configuration for wired network"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
360
                $encryptionTypeString = "any";
361
                $setupModesString = "
362
               <key>SetupModes</key>
363
                  <array>
364
                     <string>System</string>
365
                  </array>";
366
                $wifiNetworkIdentification = "";
367
                break;
368
            case mobileconfigSuperclass::NETWORK_BLOCK_TYPE_CONSORTIUMOIS:
369
                $payloadIdentifier = "hs20";
370
                $payloadShortName = _("Hotspot 2.0 Settings");
371
                $payloadName = sprintf(_("%s Hotspot 2.0 configuration"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
372
                $encryptionTypeString = "WPA";
373
                $setupModesString = "";
374
                $wifiNetworkIdentification = $this->passPointBlock($toBeConfigured);
375
                break;
376
            default:
377
                throw new Exception("This type of network block is unknown!");
378
        }
379
        $retval = "<dict>";
380
        $retval .= $this->eapBlock($eapType);
381
        $retval .= "<key>EncryptionType</key>
382
                  <string>$encryptionTypeString</string>
383
               <key>HIDDEN_NETWORK</key>
384
                  <true />
385
               <key>PayloadDescription</key>
386
                  <string>$payloadName</string>
387
               <key>PayloadDisplayName</key>
388
                  <string>$payloadShortName</string>
389
               <key>PayloadIdentifier</key>
390
                  <string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.$this->lang.$payloadIdentifier</string>
391
               <key>PayloadOrganization</key>
392
                  <string>" . $this->massagedConsortium . ".1x-config.org</string>
393
               <key>PayloadType</key>
394
                  <string>com.apple." . ($blocktype == mobileconfigSuperclass::NETWORK_BLOCK_TYPE_WIRED ? "firstactiveethernet" : "wifi") . ".managed</string>";
395
        $retval .= $this->proxySettings();
396
        $retval .= $setupModesString;
397
        if ($eapType['INNER'] == \core\common\EAP::NE_SILVERBULLET) {
398
            if ($this->clientCertUUID === NULL) {
399
                throw new Exception("Silverbullet REQUIRES a client certificate and we need to know the UUID!");
400
            }
401
            $retval .= "<key>PayloadCertificateUUID</key>
402
                        <string>$this->clientCertUUID</string>";
403
        }
404
        $retval .= "
405
               <key>PayloadUUID</key>
406
                  <string>" . \core\common\Entity::uuid() . "</string>
407
               <key>PayloadVersion</key>
408
                  <integer>1</integer>
409
                  $wifiNetworkIdentification</dict>";
410
        $this->serial = $this->serial + 1;
411
        return $retval;
412
    }
413
414
    private function removenetworkBlock($ssid) {
415
        $retval = "
416
<dict>
417
	<key>AutoJoin</key>
418
	<false/>
419
	<key>EncryptionType</key>
420
	<string>None</string>
421
	<key>HIDDEN_NETWORK</key>
422
	<false/>
423
	<key>IsHotspot</key>
424
	<false/>
425
	<key>PayloadDescription</key>
426
	<string>" . sprintf(_("This SSID should not be used after bootstrapping %s"), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']) . "</string>
427
	<key>PayloadDisplayName</key>
428
	<string>" . _("Disabled WiFi network") . "</string>
429
	<key>PayloadIdentifier</key>
430
	<string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.$this->lang.wifi.disabled.$this->removeSerial</string>
431
	<key>PayloadType</key>
432
	<string>com.apple.wifi.managed</string>
433
	<key>PayloadUUID</key>
434
	<string>" . \core\common\Entity::uuid() . "</string>
435
	<key>PayloadVersion</key>
436
	<real>1</real>";
437
        $retval .= $this->proxySettings();
438
        $retval .= "<key>SSID_STR</key>
439
	<string>$ssid</string>
440
</dict>
441
";
442
        return $retval;
443
    }
444
445
    const NETWORK_BLOCK_TYPE_SSID = 100;
446
    const NETWORK_BLOCK_TYPE_CONSORTIUMOIS = 101;
447
    const NETWORK_BLOCK_TYPE_WIRED = 102;
448
449
    private function allNetworkBlocks() {
450
        $retval = "";
451
        $this->serial = 0;
452
453
        foreach (array_keys($this->attributes['internal:SSID']) as $ssid) {
454
            $retval .= $this->networkBlock(mobileconfigSuperclass::NETWORK_BLOCK_TYPE_SSID, $ssid);
455
        }
456
        if (isset($this->attributes['media:wired']) && get_class($this) == "Device_mobileconfig_os_x") {
457
            $retval .= $this->networkBlock(mobileconfigSuperclass::NETWORK_BLOCK_TYPE_WIRED, TRUE);
458
        }
459
        if (count($this->attributes['internal:consortia']) > 0) {
460
            $retval .= $this->networkBlock(mobileconfigSuperclass::NETWORK_BLOCK_TYPE_CONSORTIUMOIS, $this->attributes['internal:consortia']);
461
        }
462
        if (isset($this->attributes['media:remove_SSID'])) {
463
            $this->removeSerial = 0;
464
            foreach ($this->attributes['media:remove_SSID'] as $removeSSID) {
465
                $retval .= $this->removenetworkBlock($removeSSID);
466
                $this->removeSerial = $this->removeSerial + 1;
467
            }
468
        }
469
        return $retval;
470
    }
471
472
    private function allCA() {
473
        $retval = "";
474
        $this->caSerial = 0;
475
        foreach ($this->attributes['internal:CAs'][0] as $ca) {
476
            $retval .= $this->caBlob($ca);
477
            $this->caSerial = $this->caSerial + 1;
478
        }
479
        return $retval;
480
    }
481
482
    private function clientP12Block() {
483
        if (!is_array($this->clientCert)) {
484
            throw new Exception("the client block was called but there is no client certificate!");
485
        }
486
        $binaryBlob = $this->clientCert["certdata"];
487
        $mimeBlob = base64_encode($binaryBlob);
488
        $mimeFormatted = chunk_split($mimeBlob, 52, "\r\n");
489
        $payloadUUID = \core\common\Entity::uuid('', $mimeBlob);
490
        return ["block" => "<dict>" .
491
            // we don't include the import password. It's displayed on screen, and should be input by the user.
492
            // <key>Password</key>
493
            //   <string>" . $this->clientCert['password'] . "</string>
494
            "<key>PayloadCertificateFileName</key>
495
                     <string>cert-cli.pfx</string>
496
                  <key>PayloadContent</key>
497
                     <data>
498
$mimeFormatted
499
                     </data>
500
                  <key>PayloadDescription</key>
501
                     <string>MIME Base-64 encoded PKCS#12 Client Certificate</string>
502
                  <key>PayloadDisplayName</key>
503
                     <string>" . _("eduroam user certificate") . "</string>
504
                  <key>PayloadIdentifier</key>
505
                     <string>com.apple.security.pkcs12.$payloadUUID</string>
506
                  <key>PayloadType</key>
507
                     <string>com.apple.security.pkcs12</string>
508
                  <key>PayloadUUID</key>
509
                     <string>$payloadUUID</string>
510
                  <key>PayloadVersion</key>
511
                     <integer>1</integer>
512
                </dict>",
513
            "UUID" => $payloadUUID,];
514
    }
515
516
    private function expiryBlock() {
517
        if (!is_array($this->clientCert)) {
518
            throw new Exception("the expiry block was called but there is no client certificate!");
519
        }
520
        $expiryTime = new \DateTime($this->clientCert['certObject']->expiry);
521
        return "<key>RemovalDate</key>
522
        <date>" . $expiryTime->format("Y-m-d") . "T" . $expiryTime->format("H:i:s") . "Z</date>";
523
    }
524
525
    private function caBlob($ca) {
526
        // cut lines with CERTIFICATE
527
        $stage1 = preg_replace('/-----BEGIN CERTIFICATE-----/', '', $ca['pem']);
528
        $stage2 = preg_replace('/-----END CERTIFICATE-----/', '', $stage1);
529
        $trimmedPem = trim($stage2);
530
531
        $stream = "
532
            <dict>
533
               <key>PayloadCertificateFileName</key>
534
               <string>" . $ca['uuid'] . ".der</string>
535
               <key>PayloadContent</key>
536
               <data>
537
" . $trimmedPem . "</data>
538
               <key>PayloadDescription</key>
539
               <string>" . _("Your Identity Providers Certification Authority") . "</string>
540
               <key>PayloadDisplayName</key>
541
               <string>" . _("Identity Provider's CA") . "</string>
542
               <key>PayloadIdentifier</key>
543
               <string>" . self::$iPhonePayloadPrefix . ".$this->massagedConsortium.$this->massagedCountry.$this->massagedInst.$this->massagedProfile.credential.$this->caSerial</string>
544
               <key>PayloadOrganization</key>
545
               <string>" . $this->massagedConsortium . ".1x-config.org</string>
546
               <key>PayloadType</key>
547
               <string>com.apple.security.root</string>
548
               <key>PayloadUUID</key><string>" . $ca['uuid'] . "</string>
549
               <key>PayloadVersion</key>
550
               <integer>1</integer>
551
            </dict>";
552
553
        return $stream;
554
    }
555
556
}
557