Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
21 andreas 2
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
2 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
21 andreas 18
 
2 andreas 19
#include <fstream>
20
#include <vector>
21
#include <iterator>
22
#include <unistd.h>
21 andreas 23
#include <sys/stat.h>
24
#include <sys/types.h>
22 andreas 25
#ifdef __ANDROID__
26
#include <android/log.h>
27
#include "tvalidatefile.h"
28
#include "ttpinit.h"
43 andreas 29
#include "tvalidatefile.h"
22 andreas 30
#endif
31
#include "tconfig.h"
2 andreas 32
#include "terror.h"
73 andreas 33
#include "tresources.h"
2 andreas 34
 
35
using std::string;
36
using std::ifstream;
21 andreas 37
using std::ofstream;
2 andreas 38
using std::fstream;
39
using std::vector;
21 andreas 40
using std::cout;
41
using std::cerr;
42
using std::endl;
2 andreas 43
 
21 andreas 44
/**
45
 * @struct SETTINGS
46
 * @brief The SETTINGS struct bundles the configuration options.
47
 *
48
 * This structure contains variables for all possible configuration options.
49
 * It is used by the class TConfig. Through this class it's possible to
50
 * access all configuration options.
51
 */
2 andreas 52
struct SETTINGS
53
{
21 andreas 54
    string pname{"tpanel"};     //!< Name of the program (default "tpanel")
55
    string path;                //!< The path where the configuration file is located
56
    string name;                //!< The name of the configuration file
57
    string project;             //!< The path where the original project files are located
58
    string server;              //!< The name or IP address of the server to connect
59
    int system{0};              //!< The number of the AMX system
60
    int port{0};                //!< The port number
61
    int ID{0};                  //!< the panel ID (a number starting by 10000)
62
    string ptype;               //!< The type of the panel (android, ipad, iphone, ...)
63
    string version;             //!< The "firmware" version
64
    string logFile;             //!< Optional path and name of a logfile
65
    string logLevel;            //!< The log level(s).
71 andreas 66
    uint logLevelBits;          //!< The numeric bit field of the loglevel
21 andreas 67
    bool longformat{false};     //!< TRUE = long format
68
    bool noBanner{false};       //!< Startup without showing a banner on the command line.
69
    bool certCheck{false};      //!< TRUE = Check certificate for SSL connection
24 andreas 70
    bool scale{false};          //!< TRUE = Images are scaled to fit the whole screen
35 andreas 71
    bool profiling{false};      //!< TRUE = The declaration traces meassure the time and write it to the log
51 andreas 72
    string password1;           //!< First panel password
73
    string password2;           //!< Second panel password
74
    string password3;           //!< Third panel password
75
    string password4;           //!< Fourth panel password
71 andreas 76
    string systemSound;         //!< name of the set system sound played on every touch.
77
    bool systemSoundState{false};   //!< TRUE = play systemsound on every touch
78
    string systemSingleBeep;    //!< name of the system sound file to play a single beep.
79
    string systemDoubleBeep;    //!< name of the system sound file to play a double beep.
104 andreas 80
    // SIP settings
81
    string sip_proxy;           //!< The address of the SIP proxy
82
    int sip_port{5060};         //!< Initializes the port of the SIP proxy to 5060
83
    string sip_stun;            //!< STUN address
84
    string sip_domain;          //!< Local domain
85
    string sip_user;            //!< The SIP user to connect.
86
    string sip_password;        //!< The SIP password to connect. Note: This password is saved in plain text!
87
    bool sip_enabled{false};    //!< By default SIP is disabled
2 andreas 88
};
89
 
90
typedef struct SETTINGS settings_t;
21 andreas 91
static settings_t localSettings;    //!< Global defines settings used in class TConfig.
2 andreas 92
 
21 andreas 93
/**
94
 * @brief TConfig::TConfig constructor
95
 *
96
 * @param path  A path and name of a configuration file.
97
 */
2 andreas 98
TConfig::TConfig(const std::string& path)
99
	: mPath(path)
100
{
90 andreas 101
    // Initialize the possible configuration paths
102
    mCfgPaths.push_back("/etc");
103
    mCfgPaths.push_back("/etc/tpanel");
104
    mCfgPaths.push_back("/usr/etc");
105
    mCfgPaths.push_back("/usr/etc/tpanel");
106
#ifdef __APPLE__
107
    mCfgPath.push_back("/opt/local/etc");
108
    mCfgPath.push_back("/opt/local/etc/tpanel");
109
    mCfgPath.push_back("/opt/local/usr/etc");
110
    mCfgPath.push_back("/opt/local/usr/etc/tpanel");
111
#endif
112
    if (findConfig())
113
        readConfig();
2 andreas 114
}
115
 
23 andreas 116
bool TConfig::reReadConfig()
117
{
118
    return readConfig();
119
}
120
 
21 andreas 121
/**
122
 * @brief TConfig::setProgName Sets the name of the application.
123
 * @param pname The name of the application.
124
 */
2 andreas 125
void TConfig::setProgName(const std::string& pname)
126
{
127
	localSettings.pname = pname;
128
}
129
 
21 andreas 130
/**
131
 * @brief TConfig::getProgName Retrieves the prevously stored application name.
132
 * @return The name of this application.
133
 */
2 andreas 134
std::string & TConfig::getProgName()
135
{
136
	return localSettings.pname;
137
}
138
 
21 andreas 139
/**
140
 * @brief TConfig::getChannel returns the AMX channel to use.
141
 *
142
 * The AMX channels an AMX panel can use start at 10000. This method returns
143
 * the channel number found in the configuration file. If there was no
144
 * channel defination found, it returns the default channel 10001.
145
 *
146
 * @return The AMX channel number to use.
147
 */
2 andreas 148
int TConfig::getChannel()
149
{
150
	return localSettings.ID;
151
}
152
 
21 andreas 153
/**
154
 * @brief TConfig::getConfigFileName returns the name of the configuration file.
155
 *
156
 * @return The name of the configuration file.
157
 */
2 andreas 158
std::string& TConfig::getConfigFileName()
159
{
160
	return localSettings.name;
161
}
162
 
21 andreas 163
/**
164
 * @brief TConfig::getConfigPath returns the path configuration file.
165
 *
166
 * The path was defined on the command line or found by searching the standard
167
 * directories.
168
 *
169
 * @return The path of the configuration file.
170
 */
2 andreas 171
std::string& TConfig::getConfigPath()
172
{
173
	return localSettings.path;
174
}
175
 
21 andreas 176
/**
177
 * @brief TConfig::getController returns the network name or IP address of the AMX controller.
178
 *
179
 * The network name or the IP address was read from the configuration file.
180
 *
181
 * @return The network name of the AMX controller.
182
 */
2 andreas 183
std::string& TConfig::getController()
184
{
185
	return localSettings.server;
186
}
187
 
21 andreas 188
/**
189
 * @brief TConfig::getSystem return the AMX system number.
190
 *
191
 * This number was read from the configuration file. If there was no system
192
 * number defined in the configuration file, then the default number 0 is
193
 * returned.
194
 *
195
 * @return The AMX system number.
196
 */
11 andreas 197
int TConfig::getSystem()
198
{
199
    return localSettings.system;
200
}
201
 
21 andreas 202
/**
203
 * @brief TConfig::getFirmVersion returns the version of the firmware.
204
 *
205
 * This option was read from the configuration file. There can be any version
206
 * number defined. But you must keep in mind, that the AMX controller may not
207
 * accept any number. If there was no version number defined, the standard
208
 * version number 1.0 is returned.
209
 *
210
 * @return The firmware version of this panel.
211
 */
2 andreas 212
std::string& TConfig::getFirmVersion()
213
{
214
	return localSettings.version;
215
}
216
 
21 andreas 217
/**
218
 * @brief TConfig::getLogFile the path and name of a logfile.
219
 *
220
 * If there is a logfile name defined in the configuration file, it is used
221
 * to write messages there. It depends on the _log level_ what is logged.
222
 *
223
 * @return The path and name of a logfile.
224
 */
2 andreas 225
std::string& TConfig::getLogFile()
226
{
227
	return localSettings.logFile;
228
}
229
 
21 andreas 230
/**
231
 * @brief TConfig::getLogLevel returns the defined log level.
232
 *
51 andreas 233
 * The loglevel can consist of the following values:
21 andreas 234
 *
235
 *     NONE         Logs nothing (default for Android)
236
 *     INFO         Logs only informations
237
 *     WARNING      Logs only warnings
51 andreas 238
 *     ERROR        Logs only errors
21 andreas 239
 *     TRACE        Logs only trace messages
240
 *     DEBUG        Logs only debug messages
241
 *     PROTOCOL     Logs only INFO and ERROR (default if NOT Android)
242
 *     ALL          Logs everything
243
 *
244
 * All log levels can be combined by concatenating them with the | symbol.
245
 *
246
 * @return The log level(s) as a string.
247
 */
2 andreas 248
string& TConfig::getLogLevel()
249
{
250
	return localSettings.logLevel;
251
}
252
 
21 andreas 253
/**
59 andreas 254
 * @brief TConfig::getLogLevelBits
255
 *
256
 * Returns the raw bit field defining the loglevels selected.
257
 *
258
 * @return The bit field of representing the selected log levels.
259
 */
260
uint TConfig::getLogLevelBits()
261
{
262
    return localSettings.logLevelBits;
263
}
264
/**
21 andreas 265
 * @brief TConfig::getPanelType the AMX type name of the panel.
266
 *
267
 * The type name of the panel is defined in the configuration file. If this
268
 * option was not defined, the default panel _android_ is returned.
269
 *
270
 * @return The type name of the panel.
271
 */
2 andreas 272
std::string& TConfig::getPanelType()
273
{
274
	return localSettings.ptype;
275
}
276
 
21 andreas 277
/**
278
 * @brief TConfig::getPort returnes the AMX port number to connect to.
279
 *
280
 * The port number can be defined in the configuration file. If there is no
281
 * configuration the default number 1319 is returned.
282
 *
283
 * @return The AMX network port number.
284
 */
2 andreas 285
int TConfig::getPort()
286
{
287
	return localSettings.port;
288
}
289
 
21 andreas 290
/**
291
 * @brief TConfig::getProjectPath returns the path to the AMX configuration files.
292
 *
293
 * The path was read from the configuration file. This path contains all the
294
 * files needed to display the elements of the surface.
295
 *
296
 * @return The path to the AMX configuration files.
297
 */
71 andreas 298
string& TConfig::getProjectPath()
2 andreas 299
{
300
	return localSettings.project;
301
}
302
 
71 andreas 303
string TConfig::getSystemPath(SYSTEMRESOURCE_t sres)
304
{
305
    string p;
306
 
307
    switch(sres)
308
    {
309
        case BORDERS:   p = "/borders"; break;
310
        case FONTS:     p = "/fonts"; break;
311
        case IMAGES:    p = "/images"; break;
312
        case SLIDERS:   p = "/sliders"; break;
313
        case SOUNDS:    p = "/sounds"; break;
314
        default:
315
            p.clear();
316
    }
317
 
318
    return localSettings.project + "/__system/graphics" + p;
319
}
320
 
23 andreas 321
bool TConfig::saveLogFile(const string &file)
322
{
323
    DECL_TRACER("TConfig::saveLogFile(const string &file)");
324
 
325
    if (file.empty() || localSettings.logFile.compare(file) == 0)
326
        return false;
327
 
328
    localSettings.logFile = file;
329
    return true;
330
}
331
 
332
bool TConfig::saveLogLevel(const string &level)
333
{
334
    DECL_TRACER("TConfig::saveLogLevel(const string &level)");
335
 
59 andreas 336
    if (level.find(SLOG_NONE) == string::npos && level.find(SLOG_INFO) == string::npos && level.find(SLOG_WARNING) == string::npos &&
337
            level.find(SLOG_ERROR) == string::npos && level.find(SLOG_TRACE) == string::npos && level.find(SLOG_DEBUG) == string::npos &&
338
            level.find(SLOG_PROTOCOL) == string::npos && level.find(SLOG_ALL) == string::npos)
23 andreas 339
        return false;
340
 
341
    localSettings.logLevel = level;
59 andreas 342
    localSettings.logLevelBits = logLevelStrToBits(level);
343
    MSG_INFO("New log level: " << level);
23 andreas 344
    return true;
345
}
346
 
59 andreas 347
bool TConfig::saveLogLevel(uint level)
348
{
349
    DECL_TRACER("TConfig::saveLogLevel(uint level)");
350
 
351
    if (level != 0 && !(level&HLOG_INFO) && !(level&HLOG_WARNING) &&
352
            !(level&HLOG_ERROR) && !(level&HLOG_TRACE) && !(level&HLOG_DEBUG))
353
        return false;
354
 
355
    localSettings.logLevelBits = level;
356
    localSettings.logLevel = logLevelBitsToString(level);
357
    MSG_INFO("New log level from bits: " << localSettings.logLevel);
358
    return true;
359
}
360
 
23 andreas 361
bool TConfig::saveChannel(int channel)
362
{
363
    DECL_TRACER("TConfig::saveChannel(int channel)");
364
 
365
    if (channel < 10000 || channel > 12000)
366
        return false;
367
 
368
    localSettings.ID = channel;
369
    return true;
370
}
371
 
372
bool TConfig::saveController(const string &cnt)
373
{
374
    DECL_TRACER("TConfig::saveController(const string &cnt)");
375
 
376
    localSettings.server = cnt;
377
    return true;
378
}
379
 
380
bool TConfig::savePanelType(const string &pt)
381
{
382
    DECL_TRACER("TConfig::savePanelType(const string &pt)");
383
 
384
    localSettings.ptype = pt;
385
    return true;
386
}
387
 
388
bool TConfig::savePort(int port)
389
{
390
    DECL_TRACER("TConfig::savePort(int port)");
391
 
392
    if (port < 1024 || port > 32767)
393
        return false;
394
 
395
    localSettings.port = port;
396
    return true;
397
}
398
 
399
bool TConfig::saveProjectPath(const string &path)
400
{
401
    DECL_TRACER("TConfig::saveProjectPath(const string &path)");
402
 
403
    if (path.empty())
404
        return false;
405
 
406
    localSettings.project = path;
407
    return true;
408
}
409
 
410
void TConfig::saveFormat(bool format)
411
{
412
    DECL_TRACER("TConfig::saveFormat(bool format)");
413
 
414
    localSettings.longformat = format;
415
}
416
 
24 andreas 417
void TConfig::saveScale(bool scale)
418
{
419
    DECL_TRACER("TConfig::saveScale(bool scale)");
420
 
421
    localSettings.scale = scale;
422
}
423
 
35 andreas 424
void TConfig::saveProfiling(bool prof)
425
{
426
    localSettings.profiling = prof;
427
}
428
 
51 andreas 429
void TConfig::savePassword1(const std::string& pw)
430
{
431
    localSettings.password1 = pw;
432
}
433
 
434
void TConfig::savePassword2(const std::string& pw)
435
{
436
    localSettings.password2 = pw;
437
}
438
 
439
void TConfig::savePassword3(const std::string& pw)
440
{
441
    localSettings.password3 = pw;
442
}
443
 
444
void TConfig::savePassword4(const std::string& pw)
445
{
446
    localSettings.password4 = pw;
447
}
448
 
71 andreas 449
void TConfig::saveSystemSoundFile(const std::string& snd)
450
{
451
    localSettings.systemSound = snd;
452
}
453
 
454
void TConfig::saveSystemSoundState(bool state)
455
{
456
    localSettings.systemSoundState = state;
457
}
458
 
104 andreas 459
std::string& TConfig::getSIPproxy()
460
{
461
    return localSettings.sip_proxy;
462
}
463
 
464
void TConfig::setSIPproxy(const std::string& address)
465
{
466
    localSettings.sip_proxy = address;
467
}
468
 
469
int TConfig::getSIPport()
470
{
471
    return localSettings.sip_port;
472
}
473
 
474
void TConfig::setSIPport(int port)
475
{
476
    localSettings.sip_port = port;
477
}
478
 
479
std::string& TConfig::getSIPstun()
480
{
481
    return localSettings.sip_stun;
482
}
483
 
484
void TConfig::setSIPstun(const std::string& address)
485
{
486
    localSettings.sip_stun = address;
487
}
488
 
489
std::string& TConfig::getSIPdomain()
490
{
491
    return localSettings.sip_domain;
492
}
493
 
494
void TConfig::setSIPdomain(const std::string& domain)
495
{
496
    localSettings.sip_domain = domain;
497
}
498
 
499
std::string& TConfig::getSIPuser()
500
{
501
    return localSettings.sip_user;
502
}
503
 
504
void TConfig::setSIPuser(const std::string& user)
505
{
506
    localSettings.sip_user = user;
507
}
508
 
509
std::string& TConfig::getSIPpassword()
510
{
511
    return localSettings.sip_password;
512
}
513
 
514
void TConfig::setSIPpassword(const std::string& pw)
515
{
516
    localSettings.sip_password = pw;
517
}
518
 
519
bool TConfig::getSIPstatus()
520
{
521
    return localSettings.sip_enabled;
522
}
523
 
524
void TConfig::setSIPstatus(bool state)
525
{
526
    localSettings.sip_enabled = state;
527
}
528
 
23 andreas 529
bool TConfig::saveSettings()
530
{
531
    DECL_TRACER("TConfig::saveSettings()");
532
 
533
    try
534
    {
535
        string fname = localSettings.path + "/" + localSettings.name;
536
        ofstream file(fname);
537
        string lines = "LogFile=" + localSettings.logFile + "\n";
538
        lines += "LogLevel=" + localSettings.logLevel + "\n";
539
        lines += "ProjectPath=" + localSettings.project + "\n";
540
        lines += string("NoBanner=") + (localSettings.noBanner ? "true" : "false") + "\n";
541
        lines += string("LongFormat=") + (localSettings.longformat ? "true" : "false") + "\n";
542
        lines += "Address=" + localSettings.server + "\n";
543
        lines += "Port=" + std::to_string(localSettings.port) + "\n";
544
        lines += "Channel=" + std::to_string(localSettings.ID) + "\n";
545
        lines += "System=" + std::to_string(localSettings.system) + "\n";
546
        lines += "PanelType=" + localSettings.ptype + "\n";
547
        lines += "Firmware=" + localSettings.version + "\n";
548
        lines += string("CertCheck=") + (localSettings.certCheck ? "true" : "false") + "\n";
24 andreas 549
        lines += string("Scale=") + (localSettings.scale ? "true" : "false") + "\n";
35 andreas 550
        lines += string("Profiling=") + (localSettings.profiling ? "true" : "false") + "\n";
51 andreas 551
        lines += string("Password1=") + localSettings.password1 + "\n";
552
        lines += string("Password2=") + localSettings.password2 + "\n";
553
        lines += string("Password3=") + localSettings.password3 + "\n";
554
        lines += string("Password4=") + localSettings.password4 + "\n";
71 andreas 555
        lines += string("SystemSoundFile=") + localSettings.systemSound + "\n";
556
        lines += string("SystemSoundState=") + (localSettings.systemSoundState ? "ON" : "OFF") + "\n";
557
        lines += string("SystemSingleBeep=") + localSettings.systemSingleBeep + "\n";
558
        lines += string("SystemDoubleBeep=") + localSettings.systemDoubleBeep + "\n";
104 andreas 559
        // SIP settings
560
        lines += string("SIP_DOMAIN") + localSettings.sip_domain + "\n";
561
        lines += string("SIP_PROXY") + localSettings.sip_password + "\n";
562
        lines += string("SIP_PORT") + std::to_string(localSettings.sip_port) + "\n";
563
        lines += string("SIP_STUN") + localSettings.sip_stun + "\n";
564
        lines += string("SIP_USER") + localSettings.sip_user + "\n";
565
        lines += string("SIP_PASSWORD") + localSettings.sip_password + "\n";
566
        lines += string("SIP_ENABLED") + (localSettings.sip_enabled ? "true" : "false") + "\n";
23 andreas 567
        file.write(lines.c_str(), lines.size());
568
        file.close();
59 andreas 569
        MSG_INFO("Actual log level: " << localSettings.logLevel);
23 andreas 570
    }
571
    catch (std::exception& e)
572
    {
573
        MSG_ERROR("Couldn't write configs: " << e.what());
574
        return false;
575
    }
576
 
577
    TError::Current()->setLogFile(localSettings.logFile);
578
    TError::Current()->setLogLevel(localSettings.logLevel);
579
    return true;
580
}
581
 
21 andreas 582
/**
583
 * @brief TConfig::isLongFormat defines the format in the logfile.
584
 *
585
 * If this returns `true` the format in the logfile is a long format. This
586
 * means, that in front of each message is an additional timestamp.
587
 *
588
 * @return `true` = long format, `false` = short format (default).
589
 */
2 andreas 590
bool TConfig::isLongFormat()
591
{
592
	return localSettings.longformat;
593
}
594
 
21 andreas 595
/**
596
 * @brief TConfig::showBanner defines whether the banner should be showed or not.
597
 *
598
 * If this method returns `false` the banner on startup is not displayed.
599
 *
600
 * @return `true` = display the banner (default), `false` = show no banner.
601
 */
2 andreas 602
bool TConfig::showBanner()
603
{
604
	return !localSettings.noBanner;
605
}
606
 
21 andreas 607
/**
24 andreas 608
 * @brief TConfig::getScale returns the scale setting
609
 *
610
 * If this is set to TRUE, all images are scaled to fit the screen.
611
 *
612
 * @return scale state
613
 */
614
bool TConfig::getScale()
615
{
616
    return localSettings.scale;
617
}
618
 
35 andreas 619
bool TConfig::getProfiling()
620
{
621
    return localSettings.profiling;
622
}
623
 
51 andreas 624
string & TConfig::getPassword1()
625
{
626
    return localSettings.password1;
627
}
628
 
629
string & TConfig::getPassword2()
630
{
631
    return localSettings.password2;
632
}
633
 
634
string & TConfig::getPassword3()
635
{
636
    return localSettings.password3;
637
}
638
 
639
string & TConfig::getPassword4()
640
{
641
    return localSettings.password4;
642
}
643
 
71 andreas 644
string & TConfig::getSystemSound()
645
{
646
    return localSettings.systemSound;
647
}
648
 
649
bool TConfig::getSystemSoundState()
650
{
651
    return localSettings.systemSoundState;
652
}
653
 
654
string& TConfig::getSingleBeepSound()
655
{
656
    return localSettings.systemSingleBeep;
657
}
658
 
659
string& TConfig::getDoubleBeepSound()
660
{
661
    return localSettings.systemDoubleBeep;
662
}
663
 
24 andreas 664
/**
71 andreas 665
 * Checks a string if it contains a word which may be interpreted as a
666
 * boolean TRUE.
667
 *
668
 * @param boolean   A string containg a word.
669
 *
670
 * @return If the string \p boolean contains a word that can be interpreted as
671
 * a boolean TRUE, it returns TRUE. In any other case it returns FALSE.
672
 */
673
bool TConfig::isTrue(const string& boolean)
674
{
675
    if (caseCompare(boolean, "1") == 0 || caseCompare(boolean, "yes") == 0 ||
676
        caseCompare(boolean, "true") == 0 || caseCompare(boolean, "on") == 0)
677
        return true;
678
 
679
    return false;
680
}
681
 
682
/**
21 andreas 683
 * @brief TConfig::certCheck check the certificate if the connection is encrypted.
684
 *
685
 * Currently not implemented!
686
 *
687
 * @return `true` = check the certificate, `false` = accept any certificate (default).
688
 */
689
bool TConfig::certCheck()
690
{
691
    return localSettings.certCheck;
692
}
693
 
694
/**
59 andreas 695
 * @brief TConfig::logLevelStrToBits
696
 * Converts a string containing one or more loglevels into a bit field.
697
 * @param level A string containing the log levels
698
 * @return A bit field representing the log levels.
699
 */
700
uint TConfig::logLevelStrToBits(const string& level)
701
{
702
    uint l = 0;
703
 
704
    if (level.find("INFO") != string::npos)
705
        l |= HLOG_INFO;
706
 
707
    if (level.find("WARNING") != string::npos)
708
        l |= HLOG_WARNING;
709
 
710
    if (level.find("ERROR") != string::npos)
711
        l |= HLOG_ERROR;
712
 
713
    if (level.find("TRACE") != string::npos)
714
        l |= HLOG_TRACE;
715
 
716
    if (level.find("DEBUG") != string::npos)
717
        l |= HLOG_DEBUG;
718
 
719
    if (level.find("PROTOCOL") != string::npos)
71 andreas 720
        l |= HLOG_PROTOCOL;
59 andreas 721
 
722
    if (level.find("ALL") != string::npos)
723
        l = HLOG_ALL;
724
 
725
    return l;
726
}
727
 
728
string TConfig::logLevelBitsToString(uint level)
729
{
730
    string l;
731
 
732
    if (level == 0)
733
    {
734
        l = SLOG_NONE;
735
        return l;
736
    }
737
 
738
    if (level & HLOG_INFO)
739
    {
740
        if (l.length() > 0)
741
            l.append("|");
742
 
743
        l.append(SLOG_INFO);
744
    }
745
 
746
    if (level & HLOG_WARNING)
747
    {
748
        if (l.length() > 0)
749
            l.append("|");
750
 
751
        l.append(SLOG_WARNING);
752
    }
753
 
754
    if (level & HLOG_ERROR)
755
    {
756
        if (l.length() > 0)
757
            l.append("|");
758
 
759
        l.append(SLOG_ERROR);
760
    }
761
 
762
    if (level & HLOG_TRACE)
763
    {
764
        if (l.length() > 0)
765
            l.append("|");
766
 
767
        l.append(SLOG_TRACE);
768
    }
769
 
770
    if (level & HLOG_DEBUG)
771
    {
772
        if (l.length() > 0)
773
            l.append("|");
774
 
775
        l.append(SLOG_DEBUG);
776
    }
777
 
778
    return l;
779
}
780
/**
21 andreas 781
 * @brief TConfig::findConfig search for the location of the configuration file.
782
 *
783
 * If there was no configuration file given on the command line, this method
784
 * searches for a configuration file on a few standard directories. This are:
785
 *
786
 *     /etc/tpanel.conf
787
 *     /etc/tpanel/tpanel.conf
788
 *     /usr/etc/tpanel.conf
789
 *     $HOME/.tpanel.conf
790
 *
791
 * On macOS the following additional directories are searched:
792
 *
793
 *     /opt/local/etc/tpanel.conf
794
 *     /opt/local/etc/tpanel/tpanel.conf
795
 *     /opt/local/usr/etc/tpanel.conf
796
 *
797
 * @return On success `true`, otherwise `false`.
798
 */
2 andreas 799
bool TConfig::findConfig()
800
{
90 andreas 801
    char *HOME = nullptr;
802
    string sFileName;
22 andreas 803
 
90 andreas 804
    if (!mPath.empty())
805
    {
806
        size_t pos = mPath.find_last_of("/");
2 andreas 807
 
90 andreas 808
        if (pos != string::npos)
809
        {
810
            localSettings.path = mPath.substr(0, pos);
811
            localSettings.name = mPath.substr(pos+1);
812
            mCFile = mPath;
813
            return !mCFile.empty();
814
        }
2 andreas 815
 
90 andreas 816
        localSettings.name = mPath;
817
        mCFile = mPath;
818
        return !mCFile.empty();
819
    }
2 andreas 820
 
90 andreas 821
    localSettings.name = "tpanel.conf";
51 andreas 822
    localSettings.password1 = "1988";
823
    localSettings.password2 = "1988";
824
    localSettings.password3 = "1988";
825
    localSettings.password4 = "1988";
71 andreas 826
    localSettings.systemSound = "singleBeep.wav";
827
    localSettings.systemSoundState = true;
828
    localSettings.systemSingleBeep = "singleBeep01.wav";
829
    localSettings.systemDoubleBeep = "doubleBeep01.wav";
21 andreas 830
#ifdef __ANDROID__
22 andreas 831
    std::stringstream s;
832
    TValidateFile vf;
833
    HOME = getenv("HOME");
2 andreas 834
 
22 andreas 835
    if (!HOME || !*HOME)
21 andreas 836
    {
43 andreas 837
        s << "Error: Environment variable HOME does not exist!";
22 andreas 838
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "%s", s.str().c_str());
839
        TError::setErrorMsg(s.str());
21 andreas 840
        return false;
841
    }
842
 
22 andreas 843
    localSettings.path = HOME;
844
 
88 andreas 845
    if (!vf.isValidDir(localSettings.path))
21 andreas 846
    {
22 andreas 847
        s << "Error: Path " << localSettings.path << " does not exist!";
848
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "%s", s.str().c_str());
849
        TError::setErrorMsg(s.str());
21 andreas 850
        return false;
851
    }
852
 
22 andreas 853
    sFileName = localSettings.path + "/" + localSettings.name;
21 andreas 854
 
22 andreas 855
    if (access(sFileName.c_str(), F_OK) == -1)    // Does the configuration file exist?
21 andreas 856
    {                                       // No, than create it and also the directory structure
857
        try
858
        {
859
            ofstream cfg(sFileName);
860
 
861
            string content = "LogFile=" + localSettings.path + "/tpanel.log\n";
23 andreas 862
            content += "LogLevel=NONE\n";
21 andreas 863
            content += "ProjectPath=" + localSettings.path + "/tpanel\n";
864
            content += "LongFormat=false\n";
865
            content += "Address=0.0.0.0\n";
866
            content += "Port=1319\n";
867
            content += "Channel=10001\n";
868
            content += "PanelType=Android\n";
26 andreas 869
            content += string("Firmware=") + VERSION_STRING() + "\n";
38 andreas 870
            content += "Scale=true\n";
35 andreas 871
            content += "Profiling=false\n";
51 andreas 872
            content += "Password1=1988\n";
873
            content += "Password2=1988\n";
874
            content += "Password3=1988\n";
875
            content += "Password4=1988\n";
71 andreas 876
            content += "SystemSoundFile=singleBeep.wav\n";
877
            content += "SystemSoundState=ON\n";
878
            content += "SystemSingleBeep=singleBeep01.wav\n";
879
            content += "SystemDoubleBeep=doubleBeep01.wav\n";
21 andreas 880
            cfg.write(content.c_str(), content.size());
881
            cfg.close();
882
 
883
            string path = localSettings.path + "/tpanel";
22 andreas 884
            TTPInit init(path);
21 andreas 885
        }
886
        catch (std::exception& e)
887
        {
23 andreas 888
            s << "Error: " << e.what();
22 andreas 889
            __android_log_print(ANDROID_LOG_ERROR, "tpanel" , "%s", s.str().c_str());
21 andreas 890
            TError::setErrorMsg(TERRERROR, string("Error: ") + e.what());
891
            return false;
892
        }
893
    }
894
#else
90 andreas 895
    if (!(HOME = getenv("HOME")))
896
        std::cerr << "TConfig::findConfig: No environment variable HOME!" << std::endl;
2 andreas 897
 
90 andreas 898
    vector<string>::iterator iter;
899
    bool found = false;
2 andreas 900
 
90 andreas 901
    for (iter = mCfgPaths.begin(); iter != mCfgPaths.end(); ++iter)
902
    {
903
        string f = *iter + "/tpanel.conf";
904
 
905
        if (!access(f.c_str(), R_OK))
906
        {
907
            sFileName = f;
908
            localSettings.path = *iter;
909
            break;
910
        }
911
    }
912
 
913
    // The local configuration file has precedence over the global one.
914
    if (HOME)
915
    {
916
        string f = HOME;
917
        f += "/.tpanel.conf";
918
 
919
        if (!access(f.data(), R_OK))
920
        {
921
            sFileName = f;
922
            localSettings.path = HOME;
923
            found = true;
924
        }
925
    }
926
 
927
    if (!found)
928
    {
929
        std::cerr << "TConfig::findConfig: Can't find any configuration file!" << std::endl;
930
        TError::setError();
931
        sFileName.clear();
932
        localSettings.name.clear();
933
        localSettings.path.clear();
934
    }
21 andreas 935
#endif
90 andreas 936
    mCFile = sFileName;
937
    return !sFileName.empty();
2 andreas 938
}
939
 
21 andreas 940
/**
941
 * @brief TConfig::readConfig reads a config file.
942
 *
943
 * This method reads a config file and stores the known options into the
944
 * struct localSettings.
945
 *
946
 * @return `true` on success.\n
947
 * Returns `false` on error and sets the internal error.
948
 */
2 andreas 949
bool TConfig::readConfig()
950
{
35 andreas 951
    ifstream fs;
2 andreas 952
 
35 andreas 953
    // First initialize the defaults
954
    localSettings.ID = 0;
955
    localSettings.port = 1397;
956
    localSettings.ptype = "android";
957
    localSettings.version = "1.0";
958
    localSettings.longformat = false;
43 andreas 959
    localSettings.profiling = false;
71 andreas 960
    localSettings.systemSoundState = true;
961
    localSettings.systemSound = "singleBeep.wav";
962
    localSettings.systemSingleBeep = "singleBeep01.wav";
963
    localSettings.systemDoubleBeep = "doubleBeep01.wav";
43 andreas 964
#ifdef __ANDROID__
965
    localSettings.logLevel = SLOG_NONE;
59 andreas 966
    localSettings.logLevelBits = HLOG_NONE;
43 andreas 967
#else
968
    localSettings.logLevel = SLOG_PROTOCOL;
969
#endif
2 andreas 970
 
35 andreas 971
    // Now get the settings from file
972
    try
973
    {
974
        fs.open(mCFile.c_str(), fstream::in);
975
    }
976
    catch (const fstream::failure e)
977
    {
978
        std::cerr << "TConfig::readConfig: Error on file " << mCFile << ": " << e.what() << std::endl;
979
        TError::setError();
980
        return false;
981
    }
2 andreas 982
 
35 andreas 983
    for (string line; getline(fs, line);)
984
    {
985
        size_t pos;
2 andreas 986
 
35 andreas 987
        if ((pos = line.find("#")) != string::npos)
988
        {
989
            if (pos == 0)
990
                line.clear();
991
            else
992
                line = line.substr(0, pos);
993
        }
2 andreas 994
 
35 andreas 995
        if (line.empty() || line.find("=") == string::npos)
996
            continue;
2 andreas 997
 
35 andreas 998
        vector<string> parts = split(line, "=", true);
2 andreas 999
 
35 andreas 1000
        if (parts.size() == 2)
1001
        {
1002
            string left = parts[0];
1003
            string right = ltrim(parts[1]);
2 andreas 1004
 
35 andreas 1005
            if (caseCompare(left, "PORT") == 0 && !right.empty())
1006
                localSettings.port = atoi(right.c_str());
1007
            else if (caseCompare(left, "LOGFILE") == 0 && !right.empty())
1008
            {
1009
                localSettings.logFile = right;
23 andreas 1010
                TStreamError::setLogFileOnly(right);
35 andreas 1011
            }
1012
            else if (caseCompare(left, "LOGLEVEL") == 0 && !right.empty())
1013
            {
1014
                TStreamError::setLogLevel(right);
1015
                localSettings.logLevel = right;
59 andreas 1016
                localSettings.logLevelBits = logLevelStrToBits(right);
35 andreas 1017
            }
1018
            else if (caseCompare(left, "ProjectPath") == 0 && !right.empty())
43 andreas 1019
            {
35 andreas 1020
                localSettings.project = right;
43 andreas 1021
#ifdef __ANDROID__
1022
                TValidateFile vf;
1023
 
1024
                if (!vf.isValidFile(right))
1025
                {
1026
                    char *HOME = getenv("HOME");
1027
 
1028
                    if (HOME)
1029
                    {
1030
                        localSettings.project = HOME;
1031
                        localSettings.project += "/tpanel";
1032
                    }
1033
                }
1034
#endif
1035
            }
11 andreas 1036
            else if (caseCompare(left, "System") == 0 && !right.empty())
1037
                localSettings.system = atoi(right.c_str());
1038
            else if (caseCompare(left, "PanelType") == 0 && !right.empty())
35 andreas 1039
                localSettings.ptype = right;
1040
            else if (caseCompare(left, "Address") == 0 && !right.empty())
1041
                localSettings.server = right;
1042
            else if (caseCompare(left, "Firmware") == 0 && !right.empty())
1043
                localSettings.version = right;
1044
            else if (caseCompare(left, "LongFormat") == 0 && !right.empty())
71 andreas 1045
                localSettings.longformat = isTrue(right);
35 andreas 1046
            else if (caseCompare(left, "NoBanner") == 0 && !right.empty())
71 andreas 1047
                localSettings.noBanner = isTrue(right);
35 andreas 1048
            else if (caseCompare(left, "CertCheck") == 0 && !right.empty())
71 andreas 1049
                localSettings.certCheck = isTrue(right);
21 andreas 1050
            else if (caseCompare(left, "Channel") == 0 && !right.empty())
35 andreas 1051
            {
1052
                localSettings.ID = atoi(right.c_str());
2 andreas 1053
 
35 andreas 1054
                if (localSettings.ID < 10000 || localSettings.ID >= 11000)
1055
                {
1056
                    std::cerr << "TConfig::readConfig: Invalid port number " << right << std::endl;
1057
                    localSettings.ID = 0;
1058
                }
1059
            }
26 andreas 1060
            else if (caseCompare(left, "Scale") == 0 && !right.empty())
71 andreas 1061
                localSettings.scale = isTrue(right);
35 andreas 1062
            else if (caseCompare(left, "Profiling") == 0 && !right.empty())
71 andreas 1063
                localSettings.profiling = isTrue(right);
51 andreas 1064
            else if (caseCompare(left, "Password1") == 0 && !right.empty())
1065
                localSettings.password1 = right;
1066
            else if (caseCompare(left, "Password2") == 0 && !right.empty())
1067
                localSettings.password2 = right;
1068
            else if (caseCompare(left, "Password3") == 0 && !right.empty())
1069
                localSettings.password3 = right;
1070
            else if (caseCompare(left, "Password4") == 0 && !right.empty())
1071
                localSettings.password4 = right;
71 andreas 1072
            else if (caseCompare(left, "SystemSoundFile") == 0 && !right.empty())
1073
                localSettings.systemSound = right;
1074
            else if (caseCompare(left, "SystemSoundState") == 0 && !right.empty())
1075
                localSettings.systemSoundState = isTrue(right);
1076
            else if (caseCompare(left, "SystemSingleBeep") == 0 && !right.empty())
1077
                localSettings.systemSingleBeep = right;
1078
            else if (caseCompare(left, "SystemDoubleBeep") == 0 && !right.empty())
1079
                localSettings.systemDoubleBeep = right;
104 andreas 1080
            else if (caseCompare(left, "SIP_PROXY") == 0 && !right.empty())     // SIP settings starting here
1081
                localSettings.sip_proxy = right;
1082
            else if (caseCompare(left, "SIP_PORT") == 0 && !right.empty())
1083
                localSettings.sip_port = atoi(right.c_str());
1084
            else if (caseCompare(left, "SIP_STUN") == 0 && !right.empty())
1085
                localSettings.sip_stun = right;
1086
            else if (caseCompare(left, "SIP_DOMAIN") == 0 && !right.empty())
1087
                localSettings.sip_domain = right;
1088
            else if (caseCompare(left, "SIP_USER") == 0 && !right.empty())
1089
                localSettings.sip_user = right;
1090
            else if (caseCompare(left, "SIP_PASSWORD") == 0 && !right.empty())
1091
                localSettings.sip_password = right;
1092
            else if (caseCompare(left, "SIP_ENABLED") == 0 && !right.empty())
1093
                localSettings.sip_enabled = isTrue(right);
26 andreas 1094
        }
35 andreas 1095
    }
2 andreas 1096
 
35 andreas 1097
    fs.close();
2 andreas 1098
 
23 andreas 1099
    if (TStreamError::checkFilter(HLOG_DEBUG))
35 andreas 1100
    {
1101
        MSG_INFO("Selected Parameters:");
1102
        MSG_INFO("    Path to cfg.: " << localSettings.path);
1103
        MSG_INFO("    Name of cfg.: " << localSettings.name);
23 andreas 1104
#ifndef __ANDROID__
1105
        MSG_INFO("    Logfile:      " << localSettings.logFile);
1106
#endif
26 andreas 1107
        MSG_INFO("    LogLevel:     " << localSettings.logLevel);
35 andreas 1108
        MSG_INFO("    Long format:  " << (localSettings.longformat ? "YES" : "NO"));
1109
        MSG_INFO("    Project path: " << localSettings.project);
23 andreas 1110
#ifndef __ANDROID__
35 andreas 1111
        MSG_INFO("    Show banner:  " << (localSettings.noBanner ? "NO" : "YES"));
23 andreas 1112
#endif
35 andreas 1113
        MSG_INFO("    Controller:   " << localSettings.server);
1114
        MSG_INFO("    Port:         " << localSettings.port);
1115
        MSG_INFO("    Channel:      " << localSettings.ID);
1116
        MSG_INFO("    Panel type:   " << localSettings.ptype);
1117
        MSG_INFO("    Firmware ver. " << localSettings.version);
26 andreas 1118
        MSG_INFO("    Scaling:      " << (localSettings.scale ? "YES" : "NO"));
35 andreas 1119
        MSG_INFO("    Profiling:    " << (localSettings.profiling ? "YES" : "NO"));
71 andreas 1120
        MSG_INFO("    System Sound: " << localSettings.systemSound);
1121
        MSG_INFO("    Sound state:  " << (localSettings.systemSoundState ? "ACTIVATED" : "DEACTIVATED"));
1122
        MSG_INFO("    Single beep:  " << localSettings.systemSingleBeep);
1123
        MSG_INFO("    Double beep:  " << localSettings.systemDoubleBeep);
35 andreas 1124
    }
2 andreas 1125
 
35 andreas 1126
    return true;
2 andreas 1127
}
1128
 
21 andreas 1129
/**
1130
 * @brief TConfig::split splitts a string into parts.
1131
 *
1132
 * The method splitts a string into parts separated by \p seps. It puts the
1133
 * parts into a vector array.
1134
 *
1135
 * @param str           The string to split
1136
 * @param seps          The separator(s)
1137
 * @param trimEmpty     `true` = trum the parts.
1138
 *
1139
 * @return A vector array containing the parts of the string \p str.
1140
 */
2 andreas 1141
vector<string> TConfig::split(const string& str, const string& seps, const bool trimEmpty)
1142
{
1143
	size_t pos = 0, mark = 0;
1144
	vector<string> parts;
1145
 
1146
	for (auto it = str.begin(); it != str.end(); ++it)
1147
	{
1148
		for (auto sepIt = seps.begin(); sepIt != seps.end(); ++sepIt)
1149
		{
1150
			if (*it == *sepIt)
1151
			{
1152
				size_t len = pos - mark;
1153
				parts.push_back(str.substr(mark, len));
1154
				mark = pos + 1;
1155
				break;
1156
			}
1157
		}
1158
 
1159
		pos++;
1160
	}
1161
 
1162
	parts.push_back(str.substr(mark));
1163
 
1164
	if (trimEmpty)
1165
	{
1166
		vector<string> nparts;
1167
 
1168
		for (auto it = parts.begin(); it != parts.end(); ++it)
1169
		{
1170
			if (it->empty())
1171
				continue;
1172
 
1173
			nparts.push_back(*it);
1174
		}
1175
 
1176
		return nparts;
1177
	}
1178
 
1179
	return parts;
1180
}
1181
 
21 andreas 1182
/**
1183
 * @brief TConfig::caseCompare compares 2 strings
1184
 *
1185
 * This method compares 2 strings case insensitive. This means that it ignores
1186
 * the case of the letters. For example:
1187
 *
1188
 *     BLAME
1189
 *     blame
1190
 *     Blame
1191
 *
1192
 * are all the same and would return 0, which means _equal_.
1193
 *
1194
 * @param str1  1st string to compare
1195
 * @param str2  2nd string to compare
1196
 *
1197
 * @return 0 if the strings are equal\n
1198
 * less than 0 if the byte of \p str1 is bigger than the byte of \p str2\n
1199
 * grater than 0 if the byte of \p str1 is smaller than the byte of \p str2.
1200
 */
2 andreas 1201
int TConfig::caseCompare(const string& str1, const string& str2)
1202
{
1203
	size_t i = 0;
1204
 
1205
	if (str1.length() != str2.length())
1206
		return ((str1.length() < str2.length()) ? -1 : 1);
1207
 
1208
	for (auto it = str1.begin(); it != str1.end(); ++it)
1209
	{
1210
		if (tolower(*it) != tolower(str2.at(i)))
1211
			return (int)(*it - str2.at(i));
1212
 
1213
		i++;
1214
	}
1215
 
1216
	return 0;
1217
}