Passed
Push — develop ( a04425...8843c6 )
by Nikolay
05:16 queued 14s
created

SystemMessages::showSSHCredentials()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 20
c 1
b 0
f 0
dl 0
loc 26
rs 9.2888
cc 5
nc 4
nop 1
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2024 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\System;
21
22
use MikoPBX\Common\Models\LanInterfaces;
23
use MikoPBX\Common\Models\PbxSettings;
24
use MikoPBX\Common\Models\PbxSettingsConstants;
25
use MikoPBX\Common\Providers\LoggerProvider;
26
use Phalcon\Di;
27
28
/**
29
 * SystemMessages class
30
 *
31
 * @package MikoPBX\Core\System
32
 *
33
 */
34
class SystemMessages extends Di\Injectable
35
{
36
    public const RESULT_DONE = 'Done';
37
    public const RESULT_FAILED = 'Failed';
38
    public const RESULT_SKIPPED = 'Skipped';
39
40
    private static array $defaultTexts = [
41
        self::RESULT_DONE => " \033[32;1mDONE\033[0m \n", // Green for DONE
42
        self::RESULT_FAILED => " \033[31;1mFAIL\033[0m \n",  // Red for FAILED
43
        self::RESULT_SKIPPED => " \033[33;1mSKIP\033[0m \n", // Yellow for SKIPPED
44
    ];
45
46
    /**
47
     * Echoes a result message with progress dots.
48
     *
49
     * @param string $message The result message to echo.
50
     * @param string $result The result status (DONE by default).
51
     *
52
     * @return void
53
     */
54
    public static function teletypeEchoResult(string $message, string $result = self::RESULT_DONE): void
55
    {
56
        $len = max(0, 80 - strlen($message) - 9);
57
        $spaces = str_repeat('.', $len);
58
        $formattedResult = self::getFormattedResult($result);
59
        self::echoToTeletype($spaces . $formattedResult);
60
    }
61
62
    /**
63
     * Echoes a message and logs it to the ttyS0-ttyS5.
64
     *
65
     * @param string $message The message to echo in a serial console.
66
     *
67
     * @return void
68
     */
69
    public static function echoToTeletype(string $message): void
70
    {
71
        // Log to serial tty
72
        for ($i = 0; $i <= 5; $i++) {
73
            $device = "/dev/ttyS$i";
74
            $setserial = Util::which('setserial');
75
            $grep = Util::which('grep');
76
            // Get the result of the command execution
77
            $result = shell_exec("$setserial -g \"$device\" | $grep -v unknown 2> /dev/null");
78
            // If the result is not empty
79
            if (!empty($result)) {
80
                // Perform the same
81
                file_put_contents($device, $message, FILE_APPEND);
82
            }
83
        }
84
    }
85
86
    /**
87
     * Prepares formatted result string.
88
     *
89
     * @param string $result The result status.
90
     *
91
     * @return string Formatted result status.
92
     */
93
    private static function getFormattedResult(string $result = self::RESULT_DONE): string
94
    {
95
        if ($result === '1') {
96
            $result = self::RESULT_DONE;
97
        } elseif ($result === '0') {
98
            $result = self::RESULT_FAILED;
99
        }
100
        if (array_key_exists($result, self::$defaultTexts)) {
101
            $resultMessage = self::$defaultTexts[$result];
102
        } else {
103
            $resultMessage = "\033[90m$result\033[0m \n"; // Grey for unknown results
104
        }
105
        return $resultMessage;
106
    }
107
108
    /**
109
     * Echoes a result message with progress dots.
110
     *
111
     * @param string $message The result message to echo.
112
     * @param string $result The result status (DONE by default).
113
     *
114
     * @return void
115
     */
116
    public static function echoResult(string $message, string $result = self::RESULT_DONE): void
117
    {
118
        $cols = self::getCountCols();
119
        if (!is_numeric($cols)) {
120
            // Failed to retrieve the screen width.
121
            return;
122
        }
123
        $len = $cols - strlen($message) - 8;
124
        if ($len < 2) {
125
            // Incorrect screen width.
126
            return;
127
        }
128
129
        $spaces = str_repeat('.', $len);
130
        $formattedResult = self::getFormattedResult($result);
131
        echo $spaces . $formattedResult;
132
    }
133
134
    /**
135
     * Gets the count of columns in the terminal window.
136
     *
137
     * @return string The count of columns.
138
     */
139
    public static function getCountCols(): string
140
    {
141
        $len = 1 * trim(shell_exec('tput cols'));
142
143
        // If the count of columns is zero, set it to a default value of 80
144
        if ($len === 0) {
145
            $len = 80;
146
        } else {
147
            // Limit the count of columns to a maximum of 80
148
            $len = min($len, 80);
149
        }
150
        return $len;
151
    }
152
153
    /**
154
     * Echoes a message and logs it to the system log.
155
     *
156
     * @param string $message The message to echo and log.
157
     *
158
     * @return void
159
     */
160
    public static function echoWithSyslog(string $message): void
161
    {
162
        echo $message;
163
        // Log the message to the system log with LOG_INFO level
164
        self::sysLogMsg(static::class, $message, LOG_INFO);
165
    }
166
167
    /**
168
     * Adds messages to Syslog.
169
     *
170
     * @param string $ident The category, class, or method identification.
171
     * @param string $message The log message.
172
     * @param int $level The log level (default: LOG_WARNING).
173
     *
174
     * @return void
175
     */
176
    public static function sysLogMsg(string $ident, string $message, int $level = LOG_WARNING): void
177
    {
178
        $logger = Di::getDefault()->getShared(LoggerProvider::SERVICE_NAME);
179
        $logger->log($level, "$message on $ident");
180
    }
181
182
183
    /**
184
     * Echoes the starting message for a stage.
185
     *
186
     * @param string $message The message to echo.
187
     */
188
    public static function echoStartMsg(string $message): void
189
    {
190
        SystemMessages::echoToTeletype($message);
191
        SystemMessages::echoWithSyslog($message);
192
    }
193
194
    /**
195
     * Echoes the result message for a stage.
196
     *
197
     * @param string $result The result of the stage.
198
     */
199
    public static function echoResultMsg(string $message, string $result = SystemMessages::RESULT_DONE): void
200
    {
201
        SystemMessages::teletypeEchoResult($message, $result);
202
        SystemMessages::echoResult($message, $result);
203
    }
204
205
    /**
206
     * Retrieves the information message containing available web interface addresses.
207
     * @param string $header Message header
208
     * @param bool $showCredentials Optional, if true the message will have the login information
209
     * @return string The information message.
210
     */
211
    public static function getInfoMessage(string $header, bool $showCredentials = false): string
212
    {
213
        $lineWidth = 70;
214
        $borderLine = str_repeat('+', $lineWidth);
215
        $emptyLine = "|" . str_repeat(' ', $lineWidth - 2) . "|";
216
        $version = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_VERSION);
217
218
        $info = PHP_EOL . $borderLine;
219
        $info .= PHP_EOL . self::formatLine($header, $lineWidth, 'center');
220
        $info .= PHP_EOL . self::formatLine("MikoPBX " . $version, $lineWidth, 'center');
221
        $info .= PHP_EOL . $borderLine;
222
223
        $addresses = self::getNetworkAddresses();
224
        $port = PbxSettings::getValueByKey(PbxSettingsConstants::WEB_HTTPS_PORT);
225
        $info .= PHP_EOL . self::formatLine("Web Interface Access", $lineWidth, 'center');
226
        $info .= PHP_EOL . $emptyLine;
227
228
        foreach (['local' => "Local Network Address:", 'external' => "External Network Address:"] as $type => $label) {
229
            if (!empty($addresses[$type])) {
230
                $info .= PHP_EOL . self::formatLine($label, $lineWidth);
231
                foreach ($addresses[$type] as $address) {
232
                    $formattedAddress = $port === '443' ? "https://$address" : "https://$address:$port";
233
                    $info .= PHP_EOL . self::formatLine($formattedAddress, $lineWidth);
234
                }
235
                $info .= PHP_EOL . $emptyLine;
236
            }
237
        }
238
239
        if ($showCredentials) {
240
            $info .= PHP_EOL . self::showWebCredentials($lineWidth);
241
            $info .= PHP_EOL . $borderLine;
242
            $info .= PHP_EOL . self::showSSHCredentials($lineWidth);
243
        }
244
245
        $info .= PHP_EOL . $emptyLine . PHP_EOL . $borderLine . PHP_EOL;
246
        return $info;
247
    }
248
249
250
    /**
251
     * Formats a given string to fit within a specified width inside a text box.
252
     *
253
     * This function formats a line by applying padding around the text to align it
254
     * according to the specified alignment parameter. It can align text to the left (default)
255
     * or center it within the line. The line is framed with vertical bars on each side.
256
     *
257
     * @param string $content The text content to be formatted within the line.
258
     * @param int $lineWidth The total width of the line, including the border characters.
259
     * @param string $align The text alignment within the line. Valid values are 'left' or 'center'.
260
     *                      Default is 'left', which aligns the text to the left with padding on the right.
261
     *                      If set to 'center', the text will be centered with padding on both sides.
262
     *
263
     * @return string The formatted line with the text aligned as specified.
264
     */
265
    private static function formatLine(string $content, int $lineWidth, string $align = 'left'): string
266
    {
267
        $padding = $lineWidth - 4 - mb_strlen($content);  // 4 characters are taken by the borders "| "
268
        if ($align === 'center') {
269
            // Center the content by splitting the padding on both sides
270
            $leftPadding = intdiv($padding, 2);
271
            $rightPadding = $padding - $leftPadding;
272
            return "| " . str_repeat(' ', $leftPadding) . $content . str_repeat(' ', $rightPadding) . " |";
273
        } else {
274
            // Left align the content (default behavior)
275
            return "| " . $content . str_repeat(' ', $padding) . " |";
276
        }
277
    }
278
279
280
    /**
281
     * Retrieves the local and external network addresses.
282
     * @return array|array[]
283
     */
284
    private static function getNetworkAddresses(): array
285
    {
286
        $addresses = ['local' => [], 'external' => []];
287
        $interfaces = LanInterfaces::find("disabled='0'");
288
        foreach ($interfaces as $interface) {
289
            if (!empty($interface->ipaddr)) {
290
                $addresses['local'][] = $interface->ipaddr;
291
            }
292
            if (!empty($interface->exthostname)) {
293
                $addresses['external'][] = strtok($interface->exthostname, ':');
294
            }
295
            if (!empty($interface->extipaddr)) {
296
                $addresses['external'][] = strtok($interface->extipaddr, ':');
297
            }
298
        }
299
        return $addresses;
300
    }
301
302
    /**
303
     * Retrieves the information message containing available web interface credentials.
304
     * @param int $lineWidth
305
     * @return string
306
     */
307
    private static function showWebCredentials(int $lineWidth): string
308
    {
309
        $cloudInstanceId = PbxSettings::getValueByKey(PbxSettingsConstants::CLOUD_INSTANCE_ID);
310
        $webAdminPassword = PbxSettings::getValueByKey(PbxSettingsConstants::WEB_ADMIN_PASSWORD);
311
        $defaultPassword = PbxSettings::getDefaultArrayValues()[PbxSettingsConstants::WEB_ADMIN_PASSWORD];
312
        if ($cloudInstanceId === $webAdminPassword || $webAdminPassword === $defaultPassword) {
313
            $adminUser = PbxSettings::getValueByKey(PbxSettingsConstants::WEB_ADMIN_LOGIN);
314
            $info = self::formatLine("Web credentials:", $lineWidth);
315
            $info .= PHP_EOL . self::formatLine("   Login: $adminUser", $lineWidth);
316
            $info .= PHP_EOL . self::formatLine("   Password: $webAdminPassword", $lineWidth);
317
        } else {
318
            $info = '';
319
        }
320
        return $info;
321
    }
322
323
    /**
324
     * Retrieves the information message containing available ssh credentials.
325
     * @param int $lineWidth
326
     * @return string
327
     */
328
    private static function showSSHCredentials(int $lineWidth): string
329
    {
330
        $cloudInstanceId = PbxSettings::getValueByKey(PbxSettingsConstants::CLOUD_INSTANCE_ID);
0 ignored issues
show
Unused Code introduced by
The assignment to $cloudInstanceId is dead and can be removed.
Loading history...
331
        $sshUser = PbxSettings::getValueByKey(PbxSettingsConstants::SSH_LOGIN);
332
        $sshPassword = PbxSettings::getValueByKey(PbxSettingsConstants::SSH_PASSWORD);
333
        $defaultSshPassword = PbxSettings::getDefaultArrayValues()[PbxSettingsConstants::SSH_PASSWORD];
334
        $sshPort = PbxSettings::getValueByKey(PbxSettingsConstants::SSH_PORT);
335
        $authorizedKeys = PbxSettings::getValueByKey(PbxSettingsConstants::SSH_AUTHORIZED_KEYS);
336
        $disablePassLogin = PbxSettings::getValueByKey(PbxSettingsConstants::SSH_DISABLE_SSH_PASSWORD);
337
338
        if ($disablePassLogin === '1' and strlen($authorizedKeys)<80){
339
            $info = self::formatLine("SSH access disabled!", $lineWidth);
340
        } else {
341
            $info = self::formatLine("SSH credentials:", $lineWidth);
342
            $info .= PHP_EOL . self::formatLine("   Port: $sshPort", $lineWidth);
343
            $info .= PHP_EOL . self::formatLine("   Login: $sshUser", $lineWidth);
344
            if ($disablePassLogin === '1') {
345
                $info .= PHP_EOL . self::formatLine("   Password access disabled, use ssh key pair.", $lineWidth);
346
            } elseif ($sshPassword === $defaultSshPassword) {
347
                $info .= PHP_EOL . self::formatLine("   Password: $sshPassword", $lineWidth);
348
            } else {
349
                $info .= PHP_EOL . self::formatLine("   Password: ***********", $lineWidth);
350
            }
351
        }
352
353
        return $info;
354
    }
355
}