gk_system.cpp 30.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 **     __                 _ _   __    __           _     _ 
 **    / _\_ __ ___   __ _| | | / / /\ \ \___  _ __| | __| |
 **    \ \| '_ ` _ \ / _` | | | \ \/  \/ / _ \| '__| |/ _` |
 **    _\ \ | | | | | (_| | | |  \  /\  / (_) | |  | | (_| |
 **    \__/_| |_| |_|\__,_|_|_|   \/  \/ \___/|_|  |_|\__,_|
 **                                                         
 **                  ___     _                              
 **                 /   \___| |_   ___  _____               
 **                / /\ / _ \ | | | \ \/ / _ \              
 **               / /_//  __/ | |_| |>  <  __/              
 **              /___,' \___|_|\__,_/_/\_\___|              
 **
 **
 **   If you have downloaded the source code for "Small World Deluxe" and are reading this,
 **   then thank you from the bottom of our hearts for making use of our hard work, sweat
 **   and tears in whatever you are implementing this into!
 **
Phobos D'thorga's avatar
Phobos D'thorga committed
19
 **   Copyright (C) 2020 - 2021. GekkoFyre.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 **
 **   Small World Deluxe is free software: you can redistribute it and/or modify
 **   it under the terms of the GNU General Public License as published by
 **   the Free Software Foundation, either version 3 of the License, or
 **   (at your option) any later version.
 **
 **   Small World is distributed in the hope that it will be useful,
 **   but WITHOUT ANY WARRANTY; without even the implied warranty of
 **   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **   GNU General Public License for more details.
 **
 **   You should have received a copy of the GNU General Public License
 **   along with Small World Deluxe.  If not, see <http://www.gnu.org/licenses/>.
 **
 **
 **   The latest source code updates can be obtained from [ 1 ] below at your
 **   discretion. A web-browser or the 'git' application may be required.
 **
38
 **   [ 1 ] - https://code.gekkofyre.io/amateur-radio/small-world-deluxe
39
40
41
42
 **
 ****************************************************************************************************/

#include "src/gk_system.hpp"
43
44
#include <exception>
#include <utility>
45
#include <QMessageBox>
46
47
#include <QtGlobal>

Phobos D'thorga's avatar
   
Phobos D'thorga committed
48
49
50
51
52
#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
#include <initguid.h>
#include <netlistmgr.h>
#endif

53
54
55
56
57
58
59
60
61
62
63
using namespace GekkoFyre;
using namespace Database;
using namespace Settings;
using namespace Audio;
using namespace AmateurRadio;
using namespace Control;
using namespace Spectrograph;
using namespace System;
using namespace Events;
using namespace Logging;

64
GkSystem::GkSystem(QPointer<GekkoFyre::StringFuncs> stringFuncs, QObject *parent) : QObject(parent)
65
{
66
    setParent(parent);
67
68
    gkStringFuncs = std::move(stringFuncs);

69
    return;
70
71
72
73
74
75
}

GkSystem::~GkSystem()
{
    return;
}
76

Phobos D'thorga's avatar
   
Phobos D'thorga committed
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
 * @brief GkSystem::isInternetAvailable checks the connectivity of the user's own, local computer/machine and determines
 * if an Internet connection is readily available. If so, returns true but otherwise, it will return false. This works
 * for both Microsoft Windows and Linux operating systems.
 * @author Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @return A true boolean value will be returned if a readily available Internet connection is usable on the user's own,
 * local machine but otherwise, a false boolean value will be returned.
 */
bool GkSystem::isInternetAvailable() {
    #if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
    std::unique_ptr<INetworkListManager> inlm;
    HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_ALL, IID_INetworkListManager, (LPVOID *)&inlm);
    if (SUCCEEDED(hr)) {
        NLM_CONNECTIVITY con;
        hr = inlm->GetConnectivity(&con);
        if (hr == S_OK) {
            if (con & NLM_CONNECTIVITY_IPV4_INTERNET || con & NLM_CONNECTIVITY_IPV6_INTERNET) {
                return true;
            } else {
                return false;
            }
        }

        return true;
    }
    #elif __linux__
    //
    // TODO: Insert a check to see that `/sbin/route` is at all installed on the end-user's computer/machine, instead of blindly
    // running the command and hoping for the best!
    //
    FILE *output;
    if (!(output = popen("/sbin/route -n | grep -c '^0\\.0\\.0\\.0'", "r"))) {
        return true;
    }

    unsigned int i;
    fscanf(output, "%u", &i);
    pclose(output);

    if (i == 0) {
        return false;
    } else {
        return true;
    }
    #endif

    return false;
}

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
 * @brief GkSystem::getNumCpuCores will get the number of CPU cores on the host machine and return it as an integer, in a semi-multiplatform
 * manner.
 * @author Dirk-Jan Kroon <https://stackoverflow.com/a/3006416>.
 * @return The number of CPU cores on the host machine.
 */
qint32 GkSystem::getNumCpuCores()
{
    #ifdef WIN32
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
    #elif MACOS
    int nm[2];
    size_t len = 4;
    uint32_t count;

    nm[0] = CTL_HW; nm[1] = HW_AVAILCPU;
144
    sysctl(nm, 2, &count, &len, nullptr, 0);
145
146
147
148
149
150
151
152
153
154
155
156
157

    if(count < 1) {
        nm[1] = HW_NCPU;
        sysctl(nm, 2, &count, &len, nullptr, 0);
        if (count < 1) { count = 1; }
    }

    return count;
    #else
    return sysconf(_SC_NPROCESSORS_ONLN);
    #endif
}

158
159
160
161
162
163
164
165
166
167
168
169
170
/**
 * @brief GkSystem::renameCommsDevice adds the correct operating system path/port identifiers onto a given port number. So for
 * example, under Linux, the first port for USB becomes, `/dev/ttyUSB1`. This aids with the user in identifying the correct port
 * and also with Hamlib for making the right connection.
 * @author Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param port The port number that's to be transformed.
 * @param conn_type The type of connection we are dealing with.
 * @return The transmogrified port identifier.
 */
QString GkSystem::renameCommsDevice(const qint32 &port, const GkConnType &conn_type)
{
    std::stringstream ss;

171
    #if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
172
    ss.clear();
173
174
175
176
177
178
179
    #elif __linux__
    ss << "/dev/";
    #endif

    switch (conn_type) {
        case GkRS232:
        {
180
            #if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
181
            ss << "COM" << port;
182
183
184
185
186
187
188
            #elif __linux__
            ss << "ttyS" << port;
            #endif
            break;
        }
        case GkUSB:
        {
189
            #if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
190
            ss << "USB" << port;
191
192
193
194
195
196
197
            #elif __linux__
            ss << "ttyUSB" << port;
            #endif
            break;
        }
        case GkParallel:
        {
198
            #if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
199
            ss << "LPT" << port;
200
            #elif __linux__
201
            ss << "lp" << port;
202
203
204
205
206
            #endif
            break;
        }
        case GkGPIO:
        {
207
            #if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
208
            ss << "DIO" << port;
209
210
211
212
213
214
215
216
217
            #elif __linux__
            ss.clear();
            ss << "/sys/class/gpio/gpio" << port;
            #endif
            break;
        }
        case GkCM108:
            throw std::invalid_argument(tr("CM108 is currently not supported by %1!").arg(General::productName).toStdString());
        default:
218
            ss << "ERROR" << port;
219
220
221
222
    }

    return QString::fromStdString(ss.str());
}
223
224
225
226
227
228
229
230
231
232

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallInitialize
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallInitialize(INetFwProfile **fwProfile)
{
233
    HRESULT hr = NOERROR;
234
235
236
237
238
239
240
241
242
    INetFwMgr* fwMgr = nullptr;
    INetFwPolicy* fwPolicy = nullptr;

    Q_ASSERT(fwProfile != nullptr);
    *fwProfile = nullptr;

    // Create an instance of the firewall settings manager.
    hr = CoCreateInstance(__uuidof(NetFwMgr), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr);
    if (FAILED(hr)) {
243
244
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("CoCreateInstance failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
245
246
247
248
249
250
        goto error;
    }

    // Retrieve the local firewall policy.
    hr = fwMgr->get_LocalPolicy(&fwPolicy);
    if (FAILED(hr)) {
251
252
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("get_LocalPolicy failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
253
254
255
256
257
258
        goto error;
    }

    // Retrieve the firewall profile currently in effect.
    hr = fwPolicy->get_CurrentProfile(fwProfile);
    if (FAILED(hr)) {
259
260
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("get_CurrentProfile failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
        goto error;
    }

    error:
    // Release the local firewall policy.
    if (fwPolicy != nullptr) {
        fwPolicy->Release();
    }

    // Release the firewall settings manager.
    if (fwMgr != nullptr) {
        fwMgr->Release();
    }

    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallIsOn
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param fwProfile
 * @param fwOn
 * @return
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallIsOn(INetFwProfile *fwProfile, WINBOOL *fwOn)
{
291
    HRESULT hr = NOERROR;
292
293
294
295
296
297
298
299
300
301
    VARIANT_BOOL fwEnabled;

    Q_ASSERT(fwProfile != nullptr);
    Q_ASSERT(fwOn != nullptr);

    *fwOn = FALSE;

    // Get the current state of the firewall.
    hr = fwProfile->get_FirewallEnabled(&fwEnabled);
    if (FAILED(hr)) {
302
303
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("get_FirewallEnabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
304
305
306
307
308
309
        goto error;
    }

    // Check to see if the firewall is on.
    if (fwEnabled != VARIANT_FALSE) {
        *fwOn = TRUE;
310
        emit publishEventMsg(tr("The firewall is on."), GkSeverity::Debug, "", false, true, false, false);
311
    } else {
312
        emit publishEventMsg(tr("The firewall is off."), GkSeverity::Debug, "", false, true, false, false);
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    }

    error:
    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallAppIsEnabled
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param fwProfile
 * @param fwProcessImageFileName
 * @param fwAppEnabled
 * @return
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallAppIsEnabled(INetFwProfile *fwProfile, const wchar_t *fwProcessImageFileName, WINBOOL *fwAppEnabled)
{
333
    HRESULT hr = NOERROR;
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    BSTR fwBstrProcessImageFileName = nullptr;
    VARIANT_BOOL fwEnabled;
    INetFwAuthorizedApplication* fwApp = nullptr;
    INetFwAuthorizedApplications* fwApps = nullptr;

    Q_ASSERT(fwProfile != nullptr);
    Q_ASSERT(fwProcessImageFileName != nullptr);
    Q_ASSERT(fwAppEnabled != nullptr);

    *fwAppEnabled = FALSE;

    // Retrieve the authorized application collection.
    hr = fwProfile->get_AuthorizedApplications(&fwApps);
    if (FAILED(hr)) {
348
349
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("get_AuthorizedApplications failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
350
351
352
353
354
355
356
        goto error;
    }

    // Allocate a BSTR for the process image file name.
    fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
    if (fwBstrProcessImageFileName == nullptr) {
        hr = E_OUTOFMEMORY;
357
358
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("SysAllocString failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
359
360
361
362
363
364
365
366
367
        goto error;
    }

    // Attempt to retrieve the authorized application.
    hr = fwApps->Item(fwBstrProcessImageFileName, &fwApp);
    if (SUCCEEDED(hr)) {
        // Find out if the authorized application is enabled.
        hr = fwApp->get_Enabled(&fwEnabled);
        if (FAILED(hr)) {
368
369
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("get_Enabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
370
371
372
373
374
375
            goto error;
        }

        if (fwEnabled != VARIANT_FALSE) {
            // The authorized application is enabled.
            *fwAppEnabled = TRUE;
376
            emit publishEventMsg(tr("Authorized application %1 is enabled in the firewall.").arg(QString::fromWCharArray(fwProcessImageFileName)), GkSeverity::Debug, "", false, true, false, false);
377
        } else {
378
            emit publishEventMsg(tr("Authorized application %1 is disabled in the firewall.").arg(QString::fromWCharArray(fwProcessImageFileName)), GkSeverity::Debug, "", false, true, false, false);
379
380
381
382
        }
    } else {
        // The authorized application was not in the collection.
        hr = S_OK;
383
        emit publishEventMsg(tr("Authorized application %1 is disabled in the firewall.").arg(QString::fromWCharArray(fwProcessImageFileName)), GkSeverity::Debug, "", false, true, false, false);
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    }

    error:
    // Free the BSTR.
    SysFreeString(fwBstrProcessImageFileName);

    // Release the authorized application instance.
    if (fwApp != nullptr) {
        fwApp->Release();
    }

    // Release the authorized application collection.
    if (fwApps != nullptr) {
        fwApps->Release();
    }

    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallTurnOn
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallTurnOn(INetFwProfile *fwProfile)
{
413
    HRESULT hr = NOERROR;
414
415
416
417
418
419
420
    BOOL fwOn;

    Q_ASSERT(fwProfile != nullptr);

    // Check to see if the firewall is off.
    hr = WindowsFirewallIsOn(fwProfile, &fwOn);
    if (FAILED(hr)) {
421
422
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("WindowsFirewallIsOn failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
423
424
425
426
427
428
429
430
        goto error;
    }

    // If it is, turn it on.
    if (!fwOn) {
        // Turn the firewall on.
        hr = fwProfile->put_FirewallEnabled(VARIANT_TRUE);
        if (FAILED(hr)) {
431
432
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_FirewallEnabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
            goto error;
        }

        emit publishEventMsg(tr("The firewall is now on."), GkSeverity::Debug, "", true, true, false, false);
    }

    error:
    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallTurnOff
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param fwProfile
 * @return
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallTurnOff(INetFwProfile *fwProfile)
{
455
    HRESULT hr = NOERROR;
456
457
458
459
460
461
462
    BOOL fwOn;

    Q_ASSERT(fwProfile != nullptr);

    // Check to see if the firewall is on.
    hr = WindowsFirewallIsOn(fwProfile, &fwOn);
    if (FAILED(hr)) {
463
464
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("WindowsFirewallIsOn failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
465
466
467
468
469
470
471
472
        goto error;
    }

    // If it is, turn it off.
    if (fwOn) {
        // Turn the firewall off.
        hr = fwProfile->put_FirewallEnabled(VARIANT_FALSE);
        if (FAILED(hr)) {
473
474
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_FirewallEnabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
            goto error;
        }

        emit publishEventMsg(tr("The firewall is now off."), GkSeverity::Debug, "", true, true, false, false);
    }

    error:
    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallAddApp
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param fwProfile
 * @param fwProcessImageFileName
 * @param fwName
 * @return
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallAddApp(INetFwProfile *fwProfile, const wchar_t *fwProcessImageFileName, const wchar_t *fwName)
{
499
    HRESULT hr = NOERROR;
500
501
502
503
504
505
506
507
508
509
510
511
512
    BOOL fwAppEnabled;
    BSTR fwBstrName = nullptr;
    BSTR fwBstrProcessImageFileName = nullptr;
    INetFwAuthorizedApplication *fwApp = nullptr;
    INetFwAuthorizedApplications *fwApps = nullptr;

    Q_ASSERT(fwProfile != nullptr);
    Q_ASSERT(fwProcessImageFileName != nullptr);
    Q_ASSERT(fwName != nullptr);

    // First check to see if the application is already authorized.
    hr = WindowsFirewallAppIsEnabled(fwProfile, fwProcessImageFileName, &fwAppEnabled);
    if (FAILED(hr)) {
513
514
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("WindowsFirewallAppIsEnabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
515
516
517
518
519
520
521
522
523

        goto error;
    }

    // Only add the application if it isn't already authorized.
    if (!fwAppEnabled) {
        // Retrieve the authorized application collection.
        hr = fwProfile->get_AuthorizedApplications(&fwApps);
        if (FAILED(hr)) {
524
525
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("get_AuthorizedApplications failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
526
527
528
529
530
531
532

            goto error;
        }

        // Create an instance of an authorized application.
        hr = CoCreateInstance(__uuidof(NetFwAuthorizedApplication), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp);
        if (FAILED(hr)) {
533
534
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("CoCreateInstance failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
535
536
537
538
539
540
541
542

            goto error;
        }

        // Allocate a BSTR for the process image file name.
        fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
        if (fwBstrProcessImageFileName == nullptr) {
            hr = E_OUTOFMEMORY;
543
544
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("SysAllocString failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
545
546
547
548
549
550
551

            goto error;
        }

        // Set the process image file name.
        hr = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName);
        if (FAILED(hr)) {
552
553
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_ProcessImageFileName failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
554
555
556
557
558
559
560
561

            goto error;
        }

        // Allocate a BSTR for the application friendly name.
        fwBstrName = SysAllocString(fwName);
        if (SysStringLen(fwBstrName) == 0) {
            hr = E_OUTOFMEMORY;
562
563
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("SysAllocString failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
564
565
566
567
568
569
570

            goto error;
        }

        // Set the application friendly name.
        hr = fwApp->put_Name(fwBstrName);
        if (FAILED(hr)) {
571
572
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_Name failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
573
574
575
576
577
578
579

            goto error;
        }

        // Add the application to the collection.
        hr = fwApps->Add(fwApp);
        if (FAILED(hr)) {
580
581
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("Add failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622

            goto error;
        }

        emit publishEventMsg(tr("Authorized application %1 is now enabled in the firewall.").arg(QString::fromWCharArray(fwProcessImageFileName)), GkSeverity::Debug, "", true, true, false, false);
    }

    error:

    // Free the BSTRs.
    SysFreeString(fwBstrName);
    SysFreeString(fwBstrProcessImageFileName);

    // Release the authorized application instance.
    if (fwApp != nullptr) {
        fwApp->Release();
    }

    // Release the authorized application collection.
    if (fwApps != nullptr) {
        fwApps->Release();
    }

    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallPortIsEnabled
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param fwProfile
 * @param portNumber
 * @param ipProtocol
 * @param fwPortEnabled
 * @return
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallPortIsEnabled(INetFwProfile *fwProfile, LONG portNumber, NET_FW_IP_PROTOCOL ipProtocol, WINBOOL *fwPortEnabled)
{
623
    HRESULT hr = NOERROR;
624
    VARIANT_BOOL fwEnabled;
625
626
    INetFwOpenPort *fwOpenPort = nullptr;
    INetFwOpenPorts *fwOpenPorts = nullptr;
627
628
629
630
631
632
633
634
635

    Q_ASSERT(fwProfile != nullptr);
    Q_ASSERT(fwPortEnabled != nullptr);

    *fwPortEnabled = FALSE;

    // Retrieve the globally open ports collection.
    hr = fwProfile->get_GloballyOpenPorts(&fwOpenPorts);
    if (FAILED(hr)) {
636
637
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("get_GloballyOpenPorts failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
638
639
640
641
642
643
644
645
646
647

        goto error;
    }

    // Attempt to retrieve the globally open port.
    hr = fwOpenPorts->Item(portNumber, ipProtocol, &fwOpenPort);
    if (SUCCEEDED(hr)) {
        // Find out if the globally open port is enabled.
        hr = fwOpenPort->get_Enabled(&fwEnabled);
        if (FAILED(hr)) {
648
649
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("get_Enabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696

            goto error;
        }

        if (fwEnabled != VARIANT_FALSE) {
            // The globally open port is enabled.
            *fwPortEnabled = TRUE;
            emit publishEventMsg(tr("Port %1 is open in the firewall.").arg(QString::number(portNumber)), GkSeverity::Debug, "", true, true, false, false);
        } else {
            emit publishEventMsg(tr("Port %1 is not open in the firewall.").arg(QString::number(portNumber)), GkSeverity::Debug, "", true, true, false, false);
        }
    } else {
        // The globally open port was not in the collection.
        hr = S_OK;
        emit publishEventMsg(tr("Port %1 is not open in the firewall.").arg(QString::number(portNumber)), GkSeverity::Debug, "", true, true, false, false);
    }

    error:

    // Release the globally open port.
    if (fwOpenPort != nullptr) {
        fwOpenPort->Release();
    }

    // Release the globally open ports collection.
    if (fwOpenPorts != nullptr) {
        fwOpenPorts->Release();
    }

    return hr;
}
#endif

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::WindowsFirewallPortAdd
 * @author Microsoft Corporation <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>,
 * Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param fwProfile
 * @param portNumber
 * @param ipProtocol
 * @param name
 * @return
 * @note Exercising the Firewall using C++ <https://docs.microsoft.com/en-us/previous-versions//aa364726(v=vs.85)?redirectedfrom=MSDN>.
 */
HRESULT GkSystem::WindowsFirewallPortAdd(INetFwProfile *fwProfile, LONG portNumber, NET_FW_IP_PROTOCOL ipProtocol, const wchar_t *name)
{
697
    HRESULT hr = NOERROR;
698
699
    BOOL fwPortEnabled;
    BSTR fwBstrName = nullptr;
700
701
    INetFwOpenPort *fwOpenPort = nullptr;
    INetFwOpenPorts *fwOpenPorts = nullptr;
702
703
704
705
706
707
708

    Q_ASSERT(fwProfile != nullptr);
    Q_ASSERT(name != nullptr);

    // First check to see if the port is already added.
    hr = WindowsFirewallPortIsEnabled(fwProfile, portNumber, ipProtocol, &fwPortEnabled);
    if (FAILED(hr)) {
709
710
        auto errMsg = processHResult(hr);
        emit publishEventMsg(tr("WindowsFirewallPortIsEnabled failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
711
712
713
714
715
716
717
718
719

        goto error;
    }

    // Only add the port if it isn't already added.
    if (!fwPortEnabled) {
        // Retrieve the collection of globally open ports.
        hr = fwProfile->get_GloballyOpenPorts(&fwOpenPorts);
        if (FAILED(hr)) {
720
721
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("get_GloballyOpenPorts failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
722
723
724
725
726
727
728

            goto error;
        }

        // Create an instance of an open port.
        hr = CoCreateInstance(__uuidof(NetFwOpenPort), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwOpenPort), (void**)&fwOpenPort);
        if (FAILED(hr)) {
729
730
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("CoCreateInstance failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
731
732
733
734
735
736
737

            goto error;
        }

        // Set the port number.
        hr = fwOpenPort->put_Port(portNumber);
        if (FAILED(hr)) {
738
739
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_Port failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
740
741
742
743
744
745
746

            goto error;
        }

        // Set the IP protocol.
        hr = fwOpenPort->put_Protocol(ipProtocol);
        if (FAILED(hr)) {
747
748
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_Protocol failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
749
750
751
752
753
754
755
756

            goto error;
        }

        // Allocate a BSTR for the friendly name of the port.
        fwBstrName = SysAllocString(name);
        if (SysStringLen(fwBstrName) == 0) {
            hr = E_OUTOFMEMORY;
757
758
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("SysAllocString failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
759
760
761
762
763
764
765

            goto error;
        }

        // Set the friendly name of the port.
        hr = fwOpenPort->put_Name(fwBstrName);
        if (FAILED(hr)) {
766
767
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("put_Name failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
768
769
770
771
772
773
774

            goto error;
        }

        // Opens the port and adds it to the collection.
        hr = fwOpenPorts->Add(fwOpenPort);
        if (FAILED(hr)) {
775
776
            auto errMsg = processHResult(hr);
            emit publishEventMsg(tr("Add failed: 0x%1lx").arg(gkStringFuncs->zeroPadding(errMsg, GK_MICROSOFT_HR_ERROR_MSG_ZERO_PADDING)), GkSeverity::Fatal, "", false, true, false, true);
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801

            goto error;
        }

        emit publishEventMsg(tr("Port %1 is now open in the firewall.").arg(QString::number(portNumber)), GkSeverity::Debug, "", true, true, false, false);
    }

    error:

    // Free the BSTR.
    SysFreeString(fwBstrName);

    // Release the open port instance.
    if (fwOpenPort != nullptr) {
        fwOpenPort->Release();
    }

    // Release the globally open ports collection.
    if (fwOpenPorts != nullptr) {
        fwOpenPorts->Release();
    }

    return hr;
}
#endif
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819

#if defined(_WIN32) || defined(__MINGW64__) || defined(__CYGWIN__)
/**
 * @brief GkSystem::processHResult
 * @author Phobos A. D'thorga <phobos.gekko@gekkofyre.io>
 * @param hr
 * @return
 */
QString GkSystem::processHResult(const HRESULT &hr)
{
    _com_error err(hr);
    LPCTSTR errMsg = err.ErrorMessage();
    QString errMsgStr = (LPSTR)errMsg;

    return errMsgStr;
}

#endif