Passed
Push — master ( 7cf688...09b08b )
by Tomasz
04:22
created

WindowsCommon   C

Complexity

Total Complexity 74

Size/Duplication

Total Lines 364
Duplicated Lines 2.2 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 8
loc 364
rs 5.5244
c 0
b 0
f 0
wmc 74
lcom 1
cbo 5

18 Methods

Rating   Name   Duplication   Size   Complexity  
A prepareInstallerLang() 0 10 2
B copyBasicFiles() 0 20 11
A copyPwdFiles() 0 9 4
B copyGeantLinkFiles() 0 11 6
A echo_nsi() 0 3 1
A sprint_nsi() 0 3 1
A __construct() 0 4 3
A scaleLogo() 0 19 3
B combineLogo() 0 42 6
A signInstaller() 0 13 3
A compileNSIS() 0 10 2
C writeDeviceInfo() 0 33 7
C msInfoFile() 0 30 8
A writeAdditionalDeletes() 0 14 4
A writeClientP12File() 0 6 2
A writeTlsUserProfile() 0 3 1
A getSupport() 0 10 2
C writeNsisDefines() 8 40 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WindowsCommon 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 WindowsCommon, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * ******************************************************************************
4
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
5
 * and GN4-2 consortia
6
 *
7
 * License: see the web/copyright.php file in the file structure
8
 * ******************************************************************************
9
 */
10
11
/**
12
 * This file contains common functions needed by all Windows installers
13
 * @author Tomasz Wolniewicz <[email protected]>
14
 *
15
 * @package ModuleWriting
16
 */
17
18
namespace devices\ms;
19
use \Exception;
20
21
/**
22
 * This class defines common functions needed by all Windows installers
23
 * @author Tomasz Wolniewicz <[email protected]>
24
 *
25
 * @package ModuleWriting
26
 */
27
abstract class WindowsCommon extends \core\DeviceConfig {
28
29
    public function copyBasicFiles() {
30
        if (!($this->copyFile('wlan_test.exe') &&
31
                $this->copyFile('check_wired.cmd') &&
32
                $this->copyFile('install_wired.cmd') &&
33
                $this->copyFile('cat_bg.bmp') &&
34
                $this->copyFile('base64.nsh'))) {
35
            throw new Exception("Copying needed files (part 1) failed for at least one file!");
36
        }
37
38
        if (!($this->copyFile('cat32.ico') &&
39
                $this->copyFile('cat_150.bmp') &&
40
                $this->copyFile('WLANSetEAPUserData/WLANSetEAPUserData32.exe', 'WLANSetEAPUserData32.exe') &&
41
                $this->copyFile('WLANSetEAPUserData/WLANSetEAPUserData64.exe', 'WLANSetEAPUserData64.exe'))) {
42
            throw new Exception("Copying needed files (part 2) failed for at least one file!");
43
        }
44
        if (!$this->translateFile('common.inc', 'common.nsh', $this->codePage)) {
45
            throw new Exception("Translating needed file common.inc failed!");
46
        }
47
        return;
48
    }
49
50
    public function copyPwdFiles() {
51
        if (!($this->copyFile('Aruba_Networks_EAP-pwd_x32.msi') &&
52
                $this->copyFile('Aruba_Networks_EAP-pwd_x64.msi'))) {
53
            throw new Exception("Copying needed files (EAP-pwd) failed for at least one file!");
54
        }
55
        if (!$this->translateFile('pwd.inc', 'cat.NSI', $this->codePage)) {
56
            throw new Exception("Translating needed file pwd.inc failed!");
57
        }
58
    }
59
60
    public function copyGeantLinkFiles() {
61
        if (!($this->copyFile('GEANTLink/GEANTLink32.msi', 'GEANTLink32.msi') &&
62
                $this->copyFile('GEANTLink/GEANTLink64.msi', 'GEANTLink64.msi') &&
63
                $this->copyFile('GEANTLink/CredWrite.exe', 'CredWrite.exe') &&
64
                $this->copyFile('GEANTLink/MsiUseFeature.exe', 'MsiUseFeature.exe'))) {
65
            throw new Exception("Copying needed files (GEANTLink) failed for at least one file!");
66
        }
67
        if (!$this->translateFile('geant_link.inc', 'cat.NSI', $this->codePage)) {
68
            throw new Exception("Translating needed file geant_link.inc failed!");
69
        }
70
    }
71
72
73
    /**
74
     * function to escape double quotes in a special NSI-compatible way
75
     * 
76
     * @param string $in input string
77
     * @return string
78
     */
79
    public static function echo_nsi($in) {
80
        echo preg_replace('/"/', '$\"', $in);
81
    }
82
83
    /**
84
     * @param string $input input string
85
     * @return string
86
     */
87
    public static function sprint_nsi($input) {
88
        return preg_replace('/"/', '$\"', $input);
89
    }
90
91
    public function __construct() {
92
        parent::__construct();
93
        $this->useGeantLink = (isset($this->options['args']) && $this->options['args'] == 'gl') ? 1 : 0;
94
    }
95
96
    protected function prepareInstallerLang() {
97
        if (isset($this->LANGS[$this->languageInstance->getLang()])) {
98
            $language = $this->LANGS[$this->languageInstance->getLang()];
99
            $this->lang = $language['nsis'];
100
            $this->codePage = 'cp' . $language['cp'];
101
        } else {
102
            $this->lang = 'English';
103
            $this->codePage = 'cp1252';
104
        }
105
    }
106
107
    public function writeDeviceInfo() {
108
        $ssidCount = count($this->attributes['internal:SSID']);
109
        $out = "<p>";
110
        $out .= sprintf(_("%s installer will be in the form of an EXE file. It will configure %s on your device, by creating wireless network profiles.<p>When you click the download button, the installer will be saved by your browser. Copy it to the machine you want to configure and execute."), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], CONFIG_CONFASSISTANT['CONSORTIUM']['display_name']);
111
        $out .= "<p>";
112
        if ($ssidCount > 1) {
113
            if ($ssidCount > 2) {
114
                $out .= sprintf(_("In addition to <strong>%s</strong> the installer will also configure access to the following networks:"), implode(', ', CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) . " ";
115
            } else {
116
                $out .= sprintf(_("In addition to <strong>%s</strong> the installer will also configure access to:"), implode(', ', CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) . " ";
117
            }
118
            $out .= '<strong>' . join('</strong>, <strong>', array_diff(array_keys($this->attributes['internal:SSID']), CONFIG_CONFASSISTANT['CONSORTIUM']['ssid'])) . '</strong>';
119
            $out .= "<p>";
120
        }
121
// TODO - change this below
122
        if ($this->selectedEapObject->isClientCertRequired()) {
123
            $out .= sprintf(_("In order to connect to the network you will need an a personal certificate in the form of a p12 file. You should obtain this certificate from your %s. Consult the support page to find out how this certificate can be obtained. Such certificate files are password protected. You should have both the file and the password available during the installation process."), $this->nomenclature_inst);
124
            return($out);
125
        }
126
        // not EAP-TLS
127
        $out .= sprintf(_("In order to connect to the network you will need an account from your %s. You should consult the support page to find out how this account can be obtained. It is very likely that your account is already activated."), $this->nomenclature_inst);
128
129
        if (!$this->useGeantLink && $this->selectedEap['OUTER'] == \core\common\EAP::TTLS) {
130
            $out .= "<p>";
131
            $out .= _("When you are connecting to the network for the first time, Windows will pop up a login box, where you should enter your user name and password. This information will be saved so that you will reconnect to the network automatically each time you are in the range.");
132
            if ($ssidCount > 1) {
133
                $out .= "<p>";
134
                $out .= _("You will be required to enter the same credentials for each of the configured notworks:") . " ";
135
                $out .= '<strong>' . join('</strong>, <strong>', array_keys($this->attributes['internal:SSID'])) . '</strong>';
136
            }
137
        }
138
        return($out);
139
    }    
140
    
141
    private function scaleLogo($imagePath, $maxSize) {
142
        $imageObject = new \Imagick($imagePath);
143
        $imageSize = $imageObject->getImageGeometry();
144
        $imageMax = max($imageSize);
145
        $this->loggerInstance->debug(5, "Logo size: ");
146
        $this->loggerInstance->debug(5, $imageSize);
147
        $this->loggerInstance->debug(5, "max=$imageMax\n");
148
// resize logo if necessary
149
        if ($imageMax > $maxSize) {
150
            if ($imageMax == $imageSize['width']) {
151
                $imageObject->scaleImage($maxSize, 0);
152
            } else {
153
                $imageObject->scaleImage(0, $maxSize);
154
            }
155
        }
156
        $imageSize = $imageObject->getImageGeometry();
157
        $this->background['freeHeight'] -= $imageSize['height'];
158
        return($imageObject);
159
    }
160
161
    protected function combineLogo($logos = NULL, $fedLogo = NULL) {
162
        // maximum size to which we want to resize the logos
163
        
164
        $maxSize = 120;
165
        // $freeTop is set to how much vertical space we need to leave at the top
166
        // this will depend on the design of the background
167
        $freeTop = 70;
168
        // $freeBottom is set to how much vertical space we need to leave at the bottom
169
        // this will depend on the design of the background
170
        $freeBottom = 30;
171
        // $useFederationLogo controls if federation logos should be enabled (TRUE or FALSE)
172
        $useFederationLogo = FALSE;
173
174
        $bgImage = new \Imagick('cat_bg.bmp');
175
        $bgImage->setFormat('BMP3');
176
        $bgImageSize = $bgImage->getImageGeometry();
177
        $logosToPlace = [];
178
        $this->background = [];
179
        $this->background['freeHeight'] = $bgImageSize['height'] - $freeTop - $freeBottom;
180
181
        if ($useFederationLogo && $fedLogo != NULL) {
182
            $logosToPlace[] = $this->scaleLogo($fedLogo[0]['name'], $maxSize);
183
        }
184
        if ($logos != NULL) {
185
            $logosToPlace[] = $this->scaleLogo($logos[0]['name'], $maxSize);
186
        }
187
188
        $logoCount = count($logosToPlace);
189
        if ($logoCount > 0) {
190
            $voffset = $freeTop;
191
            $freeSpace = (int)round($this->background['freeHeight'] / ($logoCount + 1));
192
            foreach ($logosToPlace as $logo) {
193
                $voffset += $freeSpace;
194
                $logoSize = $logo->getImageGeometry();
195
                $hoffset = (int)round(($bgImageSize['width'] - $logoSize['width']) / 2);
196
                $bgImage->compositeImage($logo, $logo->getImageCompose(), $hoffset, $voffset);
197
                $voffset += $logoSize['height'];
198
                }
199
        }
200
//new image is saved as the background
201
        $bgImage->writeImage('BMP3:cat_bg.bmp');
202
    }
203
204
    protected function signInstaller() {
205
        $fileName = $this->installerBasename . '.exe';
206
        if (!$this->sign) {
207
            rename("installer.exe", $fileName);
208
            return $fileName;
209
        }
210
        // are actually signing
211
        $outputFromSigning = system($this->sign . " installer.exe '$fileName' > /dev/null");
212
        if ($outputFromSigning === FALSE) {
213
            $this->loggerInstance->debug(2, "Signing the WindowsCommon installer $fileName FAILED!\n");
214
        }
215
        return $fileName;
216
    }
217
218
    protected function compileNSIS() {
219
        if (CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
220
            $makensis = CONFIG_CONFASSISTANT['PATHS']['makensis'] . " -INPUTCHARSET UTF8";
221
        } else {
222
            $makensis = CONFIG_CONFASSISTANT['PATHS']['makensis'];
223
        }
224
        $command = $makensis . ' -V4 cat.NSI > nsis.log';
225
        system($command);
226
        $this->loggerInstance->debug(4, "compileNSIS:$command\n");
227
    }
228
229
    private function getSupport($attr, $type) {
230
        $supportString = [
231
            'email' => 'SUPPORT',
232
            'url' => 'URL',
233
        ];
234
        $s = "support_" . $type . "_substitute";
235
        $substitute = $this->translateString($this->$s, $this->codePage);
236
        $returnValue = !empty($attr['support:' . $type][0]) ? $attr['support:' .  $type][0] : $substitute;
237
        return('!define ' . $supportString[$type] . ' "' . $returnValue . '"' . "\n");
238
    }
239
    
240
    
241
    protected function writeNsisDefines($eap, $attr) {
0 ignored issues
show
Unused Code introduced by
The parameter $eap is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
242
        $fcontents = "\n" . '!define NSIS_MAJOR_VERSION ' . CONFIG_CONFASSISTANT['NSIS_VERSION'];
243
        if ($attr['internal:profile_count'][0] > 1) {
244
            $fcontents .= "\n" . '!define USER_GROUP "' . $this->translateString(str_replace('"', '$\\"', $attr['profile:name'][0]), $this->codePage) . '"
245
';
246
        }
247
        $fcontents .=  '
248
Caption "' . $this->translateString(sprintf(WindowsCommon::sprint_nsi(_("%s installer for %s")), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], $attr['general:instname'][0]), $this->codePage) . '"
249
!define APPLICATION "' . $this->translateString(sprintf(WindowsCommon::sprint_nsi(_("%s installer for %s")), CONFIG_CONFASSISTANT['CONSORTIUM']['display_name'], $attr['general:instname'][0]), $this->codePage) . '"
250
!define VERSION "' . \core\CAT::VERSION_MAJOR . '.' . \core\CAT::VERSION_MINOR . '"
251
!define INSTALLER_NAME "installer.exe"
252
!define LANG "' . $this->lang . '"
253
!define LOCALE "' . preg_replace('/\..*$/', '', CONFIG['LANGUAGES'][$this->languageInstance->getLang()]['locale']) . '"
254
;--------------------------------
255
!define ORGANISATION "' . $this->translateString($attr['general:instname'][0], $this->codePage) . '"
256
';
257
        $fcontents .= $this->getSupport($attr, 'email');
258
        $fcontents .= $this->getSupport($attr, 'url');                
259
        if (\core\common\Entity::getAttributeValue($attr, 'media:wired', 0) == 'on') {
260
            $fcontents .= '!define WIRED
261
        ';
262
        }
263
        $fcontents .= '!define PROVIDERID "urn:UUID:' . $this->deviceUUID . '"
264
';
265
        if (!empty($attr['internal:realm'][0])) {
266
            $fcontents .= '!define REALM "' . $attr['internal:realm'][0] . '"
267
';
268
        }
269 View Code Duplication
        if(!empty($attr['internal:hint_userinput_suffix'][0]) && $attr['internal:hint_userinput_suffix'][0] == 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
            $fcontents .= '!define HINT_USER_INPUT "' . $attr['internal:hint_userinput_suffix'][0] . '"
271
';
272
        }
273 View Code Duplication
        if(!empty($attr['internal:verify_userinput_suffix'][0]) && $attr['internal:verify_userinput_suffix'][0] == 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
274
            $fcontents .= '!define VERIFY_USER_REALM_INPUT "' . $attr['internal:verify_userinput_suffix'][0] . '"
275
';
276
        }
277
        $fcontents .= $this->msInfoFile($attr);
278
        return($fcontents);
279
           
280
    }
281
    
282
    protected function msInfoFile($attr) {
283
        $out = '';
284
        if (isset($attr['support:info_file'])) {
285
            $out .= '!define EXTERNAL_INFO "';
286
//  $this->loggerInstance->debug(4,"Info file type ".$attr['support:info_file'][0]['mime']."\n");
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
287
            if ($attr['internal:info_file'][0]['mime'] == 'rtf') {
288
                $out = '!define LICENSE_FILE "' . $attr['internal:info_file'][0]['name'];
289
            } elseif ($attr['internal:info_file'][0]['mime'] == 'txt') {
290
                $infoFile = file_get_contents($attr['internal:info_file'][0]['name']);
291
                if ($infoFile === FALSE) {
292
                    throw new Exception("We were told this file exists. Failing to read it is not really possible.");
293
                }
294
                if (CONFIG_CONFASSISTANT['NSIS_VERSION'] >= 3) {
295
                    $infoFileConverted = $infoFile;
296
                } else {
297
                    $infoFileConverted = iconv('UTF-8', $this->codePage . '//TRANSLIT', $infoFile);
298
                }
299
                if ($infoFileConverted !== FALSE && strlen($infoFileConverted) > 0) {
300
                    file_put_contents('info_f.txt', $infoFileConverted);
301
                    $out = '!define LICENSE_FILE " info_f.txt';
302
                }
303
            } else {
304
                $out = '!define EXTERNAL_INFO "' . $attr['internal:info_file'][0]['name'];
305
            }
306
307
            $out .= "\"\n";
308
        }
309
        $this->loggerInstance->debug(4, "Info file returned: $out");
310
        return $out;
311
    }
312
313
    protected function writeAdditionalDeletes($profiles) {
314
        if (count($profiles) == 0) {
315
            return;
316
        }
317
        $fileHandle = fopen('profiles.nsh', 'a');
318
        if ($fileHandle === FALSE) {
319
            throw new Exception("Unable to open possibly pre-existing profiles.nsh to append additional deletes.");
320
        }
321
        fwrite($fileHandle, "!define AdditionalDeletes\n");
322
        foreach ($profiles as $profile) {
323
            fwrite($fileHandle, "!insertmacro define_delete_profile \"$profile\"\n");
324
        }
325
        fclose($fileHandle);
326
    }
327
328
    protected function writeClientP12File() {
329
        if (!is_array($this->clientCert)) {
330
            throw new Exception("the client block was called but there is no client certificate!");
331
        }
332
        file_put_contents('SB_cert.p12', $this->clientCert["certdata"]);
333
    }
334
335
    protected function writeTlsUserProfile() {
336
        
337
    }
338
339
    public $LANGS = [
340
        'fr' => ['nsis' => "French", 'cp' => '1252'],
341
        'de' => ['nsis' => "German", 'cp' => '1252'],
342
        'es' => ['nsis' => "SpanishInternational", 'cp' => '1252'],
343
        'it' => ['nsis' => "Italian", 'cp' => '1252'],
344
        'nl' => ['nsis' => "Dutch", 'cp' => '1252'],
345
        'sv' => ['nsis' => "Swedish", 'cp' => '1252'],
346
        'fi' => ['nsis' => "Finnish", 'cp' => '1252'],
347
        'pl' => ['nsis' => "Polish", 'cp' => '1250'],
348
        'ca' => ['nsis' => "Catalan", 'cp' => '1252'],
349
        'sr' => ['nsis' => "SerbianLatin", 'cp' => '1250'],
350
        'hr' => ['nsis' => "Croatian", 'cp' => '1250'],
351
        'sl' => ['nsis' => "Slovenian", 'cp' => '1250'],
352
        'da' => ['nsis' => "Danish", 'cp' => '1252'],
353
        'nb' => ['nsis' => "Norwegian", 'cp' => '1252'],
354
        'nn' => ['nsis' => "NorwegianNynorsk", 'cp' => '1252'],
355
        'el' => ['nsis' => "Greek", 'cp' => '1253'],
356
        'ru' => ['nsis' => "Russian", 'cp' => '1251'],
357
        'pt' => ['nsis' => "Portuguese", 'cp' => '1252'],
358
        'uk' => ['nsis' => "Ukrainian", 'cp' => '1251'],
359
        'cs' => ['nsis' => "Czech", 'cp' => '1250'],
360
        'sk' => ['nsis' => "Slovak", 'cp' => '1250'],
361
        'bg' => ['nsis' => "Bulgarian", 'cp' => '1251'],
362
        'hu' => ['nsis' => "Hungarian", 'cp' => '1250'],
363
        'ro' => ['nsis' => "Romanian", 'cp' => '1250'],
364
        'lv' => ['nsis' => "Latvian", 'cp' => '1257'],
365
        'mk' => ['nsis' => "Macedonian", 'cp' => '1251'],
366
        'et' => ['nsis' => "Estonian", 'cp' => '1257'],
367
        'tr' => ['nsis' => "Turkish", 'cp' => '1254'],
368
        'lt' => ['nsis' => "Lithuanian", 'cp' => '1257'],
369
        'ar' => ['nsis' => "Arabic", 'cp' => '1256'],
370
        'he' => ['nsis' => "Hebrew", 'cp' => '1255'],
371
        'id' => ['nsis' => "Indonesian", 'cp' => '1252'],
372
        'mn' => ['nsis' => "Mongolian", 'cp' => '1251'],
373
        'sq' => ['nsis' => "Albanian", 'cp' => '1252'],
374
        'br' => ['nsis' => "Breton", 'cp' => '1252'],
375
        'be' => ['nsis' => "Belarusian", 'cp' => '1251'],
376
        'is' => ['nsis' => "Icelandic", 'cp' => '1252'],
377
        'ms' => ['nsis' => "Malay", 'cp' => '1252'],
378
        'bs' => ['nsis' => "Bosnian", 'cp' => '1250'],
379
        'ga' => ['nsis' => "Irish", 'cp' => '1250'],
380
        'uz' => ['nsis' => "Uzbek", 'cp' => '1251'],
381
        'gl' => ['nsis' => "Galician", 'cp' => '1252'],
382
        'af' => ['nsis' => "Afrikaans", 'cp' => '1252'],
383
        'ast' => ['nsis' => "Asturian", 'cp' => '1252'],
384
    ];
385
    public $codePage;
386
    public $lang;
387
    public $useGeantLink;
388
    private $background;
389
390
}
391