Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
22 andreas 1
/*
2
 * Copyright (C) 2021 by Andreas Theofilu <andreas@theosys.at>
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
 */
18
 
19
#include <iostream>
20
#include <fstream>
120 andreas 21
#include <functional>
22 andreas 22
 
118 andreas 23
#include <stdio.h>
50 andreas 24
#include <sys/stat.h>
25
#include <fcntl.h>
26
#include <errno.h>
118 andreas 27
#include <unistd.h>
50 andreas 28
 
22 andreas 29
#include <QFile>
43 andreas 30
#ifdef __ANDROID__
31
#include <QtAndroid>
32
#endif
33
#include <QMap>
34
#include <QHash>
22 andreas 35
 
36
#include "ttpinit.h"
37
#include "terror.h"
50 andreas 38
#include "tvalidatefile.h"
115 andreas 39
#include "tconfig.h"
40
#include "tfsfreader.h"
119 andreas 41
#include "tdirectory.h"
122 andreas 42
#include "tresources.h"
22 andreas 43
 
125 andreas 44
#if __cplusplus < 201402L
45
#   error "This module requires at least C++14 standard!"
46
#else
47
#   if __cplusplus < 201703L
48
#       include <experimental/filesystem>
49
        namespace fs = std::experimental::filesystem;
50
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
51
#   else
52
#       include <filesystem>
53
#       ifdef __ANDROID__
54
            namespace fs = std::__fs::filesystem;
55
#       else
56
            namespace fs = std::filesystem;
57
#       endif
58
#   endif
59
#endif
60
 
22 andreas 61
using std::string;
85 andreas 62
using std::vector;
22 andreas 63
using std::ostream;
120 andreas 64
using std::bind;
122 andreas 65
using std::ifstream;
22 andreas 66
 
122 andreas 67
#define SYSTEM_DEFAULT      "/.system"
68
 
51 andreas 69
TTPInit::TTPInit()
70
{
71
    DECL_TRACER("TTPInit::TTPInit()");
72
}
73
 
22 andreas 74
TTPInit::TTPInit(const string& path)
75
    : mPath(path)
76
{
77
    DECL_TRACER("TTPInit::TTPInit(const string& path)")
78
 
50 andreas 79
    createDirectoryStructure();
22 andreas 80
    createPanelConfigs();
115 andreas 81
    loadSurfaceFromController();
22 andreas 82
}
83
 
84
bool TTPInit::createPanelConfigs()
85
{
86
    DECL_TRACER("TTPInit::createPanelConfigs()");
87
 
113 andreas 88
    vector<string> resFiles = {
89
        ":ressources/external.xma",
90
        ":ressources/fnt.xma",
91
        ":ressources/icon.xma",
92
        ":ressources/_main.xml",
93
        ":ressources/manifest.xma",
94
        ":ressources/map.xma",
95
        ":ressources/pal_001.xma",
96
        ":ressources/prj.xma",
97
        ":ressources/_setup.xml",
98
        ":ressources/table.xma",
99
        ":ressources/fonts/arial.ttf",
100
        ":ressources/images/theosys_logo.png",
101
        ":ressources/__system/graphics/fonts/amxbold_.ttf",
102
        ":ressources/__system/graphics/fonts/arialbd.ttf",
103
        ":ressources/__system/graphics/fonts/arial.ttf",
104
        ":ressources/__system/graphics/fonts/cour.ttf",
105
        ":ressources/__system/graphics/sounds/audioTest.wav",
106
        ":ressources/__system/graphics/sounds/docked.mp3",
107
        ":ressources/__system/graphics/sounds/doubleBeep01.wav",
108
        ":ressources/__system/graphics/sounds/doubleBeep02.wav",
109
        ":ressources/__system/graphics/sounds/doubleBeep03.wav",
110
        ":ressources/__system/graphics/sounds/doubleBeep04.wav",
111
        ":ressources/__system/graphics/sounds/doubleBeep05.wav",
112
        ":ressources/__system/graphics/sounds/doubleBeep06.wav",
113
        ":ressources/__system/graphics/sounds/doubleBeep07.wav",
114
        ":ressources/__system/graphics/sounds/doubleBeep08.wav",
115
        ":ressources/__system/graphics/sounds/doubleBeep09.wav",
116
        ":ressources/__system/graphics/sounds/doubleBeep10.wav",
117
        ":ressources/__system/graphics/sounds/doubleBeep.wav",
118
        ":ressources/__system/graphics/sounds/ringback.wav",
119
        ":ressources/__system/graphics/sounds/ringtone.wav",
120
        ":ressources/__system/graphics/sounds/singleBeep01.wav",
121
        ":ressources/__system/graphics/sounds/singleBeep02.wav",
122
        ":ressources/__system/graphics/sounds/singleBeep03.wav",
123
        ":ressources/__system/graphics/sounds/singleBeep04.wav",
124
        ":ressources/__system/graphics/sounds/singleBeep05.wav",
125
        ":ressources/__system/graphics/sounds/singleBeep06.wav",
126
        ":ressources/__system/graphics/sounds/singleBeep07.wav",
127
        ":ressources/__system/graphics/sounds/singleBeep08.wav",
128
        ":ressources/__system/graphics/sounds/singleBeep09.wav",
129
        ":ressources/__system/graphics/sounds/singleBeep10.wav",
130
        ":ressources/__system/graphics/sounds/singleBeep.wav",
131
        ":ressources/__system/graphics/draw.xma",
132
        ":ressources/__system/graphics/fnt.xma",
133
        ":ressources/__system/graphics/version.xma"
134
    };
135
 
22 andreas 136
    bool err = false;
113 andreas 137
    vector<string>::iterator iter;
22 andreas 138
 
113 andreas 139
    for (iter = resFiles.begin(); iter != resFiles.end(); ++iter)
22 andreas 140
    {
113 andreas 141
        if (!copyFile(*iter))
50 andreas 142
            err = true;
22 andreas 143
    }
144
 
122 andreas 145
    // Mark files as system default files
146
    try
147
    {
148
        string marker = mPath + SYSTEM_DEFAULT;
149
        std::ofstream mark(marker);
150
        time_t t = time(NULL);
151
        mark.write((char *)&t, sizeof(time_t));
152
        mark.close();
153
    }
154
    catch (std::exception& e)
155
    {
156
        MSG_ERROR("Error creating a marker file: " << e.what());
157
        err = true;
158
    }
22 andreas 159
 
160
    return err;
161
}
51 andreas 162
 
117 andreas 163
bool TTPInit::createSystemConfigs()
164
{
165
    DECL_TRACER("TTPInit::createSystemConfigs()");
166
 
167
    vector<string> resFiles = {
168
        ":ressources/__system/graphics/fonts/amxbold_.ttf",
169
        ":ressources/__system/graphics/fonts/arialbd.ttf",
170
        ":ressources/__system/graphics/fonts/arial.ttf",
171
        ":ressources/__system/graphics/fonts/cour.ttf",
172
        ":ressources/__system/graphics/sounds/audioTest.wav",
173
        ":ressources/__system/graphics/sounds/docked.mp3",
174
        ":ressources/__system/graphics/sounds/doubleBeep01.wav",
175
        ":ressources/__system/graphics/sounds/doubleBeep02.wav",
176
        ":ressources/__system/graphics/sounds/doubleBeep03.wav",
177
        ":ressources/__system/graphics/sounds/doubleBeep04.wav",
178
        ":ressources/__system/graphics/sounds/doubleBeep05.wav",
179
        ":ressources/__system/graphics/sounds/doubleBeep06.wav",
180
        ":ressources/__system/graphics/sounds/doubleBeep07.wav",
181
        ":ressources/__system/graphics/sounds/doubleBeep08.wav",
182
        ":ressources/__system/graphics/sounds/doubleBeep09.wav",
183
        ":ressources/__system/graphics/sounds/doubleBeep10.wav",
184
        ":ressources/__system/graphics/sounds/doubleBeep.wav",
185
        ":ressources/__system/graphics/sounds/ringback.wav",
186
        ":ressources/__system/graphics/sounds/ringtone.wav",
187
        ":ressources/__system/graphics/sounds/singleBeep01.wav",
188
        ":ressources/__system/graphics/sounds/singleBeep02.wav",
189
        ":ressources/__system/graphics/sounds/singleBeep03.wav",
190
        ":ressources/__system/graphics/sounds/singleBeep04.wav",
191
        ":ressources/__system/graphics/sounds/singleBeep05.wav",
192
        ":ressources/__system/graphics/sounds/singleBeep06.wav",
193
        ":ressources/__system/graphics/sounds/singleBeep07.wav",
194
        ":ressources/__system/graphics/sounds/singleBeep08.wav",
195
        ":ressources/__system/graphics/sounds/singleBeep09.wav",
196
        ":ressources/__system/graphics/sounds/singleBeep10.wav",
197
        ":ressources/__system/graphics/sounds/singleBeep.wav",
198
        ":ressources/__system/graphics/draw.xma",
199
        ":ressources/__system/graphics/fnt.xma",
200
        ":ressources/__system/graphics/version.xma"
201
    };
202
 
203
    bool err = false;
204
    vector<string>::iterator iter;
205
 
206
    for (iter = resFiles.begin(); iter != resFiles.end(); ++iter)
207
    {
208
        if (!copyFile(*iter))
209
            err = true;
210
    }
211
 
212
    return err;
213
}
214
 
50 andreas 215
bool TTPInit::createDirectoryStructure()
216
{
217
    DECL_TRACER("TTPInit::createDirectoryStructure()");
218
 
51 andreas 219
    if (mPath.empty())
220
    {
221
        MSG_ERROR("Got no path to create the directory structure!");
222
        return false;
223
    }
224
 
50 andreas 225
    string pfad = mPath;
226
 
227
    if (!_makeDir(pfad))
228
        return false;
229
 
230
    pfad = mPath + "/fonts";
231
 
232
    if (!_makeDir(pfad))
233
        return false;
234
 
235
    pfad = mPath + "/images";
236
 
237
    if (!_makeDir(pfad))
238
        return false;
239
 
240
    pfad = mPath + "/sounds";
241
 
242
    if (!_makeDir(pfad))
243
        return false;
244
 
245
    pfad = mPath + "/__system";
246
 
247
    if (!_makeDir(pfad))
248
        return false;
249
 
250
    pfad = mPath + "/__system/graphics";
251
 
252
    if (!_makeDir(pfad))
253
        return false;
254
 
255
    pfad = mPath + "/__system/graphics/fonts";
256
 
257
    if (!_makeDir(pfad))
258
        return false;
259
 
260
    pfad = mPath + "/__system/graphics/images";
261
 
262
    if (!_makeDir(pfad))
263
        return false;
264
 
265
    pfad = mPath + "/__system/graphics/sounds";
266
 
267
    if (!_makeDir(pfad))
268
        return false;
269
 
270
    pfad = mPath + "/__system/graphics/cursors";
271
 
272
    if (!_makeDir(pfad))
273
        return false;
274
 
275
    pfad = mPath + "/__system/graphics/sliders";
276
 
277
    if (!_makeDir(pfad))
278
        return false;
279
 
88 andreas 280
    pfad = mPath + "/__system/graphics/borders";
281
 
282
    if (!_makeDir(pfad))
283
        return false;
284
 
50 andreas 285
    return true;
286
}
287
 
288
bool TTPInit::_makeDir(const std::string& dir)
289
{
290
    DECL_TRACER("TTPInit::_makeDir(const std::string& dir)");
291
 
292
    TValidateFile vf;
293
 
71 andreas 294
    if (!vf.isValidDir(dir))
50 andreas 295
    {
296
        if (mkdir (dir.c_str(), S_IRWXU | S_IRWXG | S_IRWXG) != 0)
297
        {
298
            MSG_ERROR("Directory " << dir << ": " << strerror(errno));
299
            return false;
300
        }
301
    }
302
 
303
    return true;
304
}
305
 
113 andreas 306
bool TTPInit::copyFile(const std::string& fname)
307
{
308
    DECL_TRACER("TTPInit::copyFile(const std::string& fname)");
309
 
310
    bool err = false;
311
    QFile external(fname.c_str());
312
    size_t pos = fname.find_first_of("/");
313
    string bname;
314
 
315
    if (pos != string::npos)
316
        bname = fname.substr(pos);
317
    else
318
        bname = fname;
319
 
320
    if (external.exists())
321
    {
322
        QString path = mPath.c_str();
323
        path += bname.c_str();
118 andreas 324
        bname = path.toStdString();
113 andreas 325
 
118 andreas 326
        // If the target already exists we must delete it first.
327
        if (access(bname.data(), F_OK) == 0)
328
            remove(bname.data());
329
 
113 andreas 330
        if (!external.copy(path))
331
        {
43 andreas 332
#ifdef __ANDROID__
113 andreas 333
            if (!askPermissions())
334
            {
335
                MSG_ERROR("Could not copy \"" << bname << "\" to " << path.toStdString() << " because permission was denied!");
336
                err = true;
337
            }
338
            else if (!external.copy(path))
339
            {
340
                MSG_ERROR("Could not copy \"" << bname << "\" to " << path.toStdString());
341
                err = true;
342
            }
343
#else
344
            MSG_ERROR("Could not copy \"" << bname << "\" to " << path.toStdString());
345
            err = true;
346
#endif
347
        }
348
    }
349
    else
350
    {
351
        MSG_ERROR("File " << external.fileName().toStdString() << " doesn't exist!");
352
        err = true;
353
    }
354
 
355
    return err;
356
}
357
 
137 andreas 358
string TTPInit::getTmpFileName()
359
{
360
    DECL_TRACER("TTPInit::getTmpFileName()");
361
 
362
    const string alphanum =
363
            "0123456789"
364
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
365
            "abcdefghijklmnopqrstuvwxyz";
366
 
367
    size_t stringLength = alphanum.length() - 1;
368
    std::string Str;
369
    char *tmp = getenv("TMP");
370
 
371
    if (!tmp)
372
        tmp = getenv("TEMP");
373
 
374
    if (!tmp)
375
        tmp = getenv("HOME");
376
    else
377
        tmp = (char *)"/tmp";
378
 
379
    Str.assign(tmp);
380
    Str.append("/");
381
 
382
    for(size_t i = 0; i < MAX_TMP_LEN; ++i)
383
        Str += alphanum[rand() % stringLength];
384
 
385
    // We create the file. YES, this is a security hole but in our case we have
386
    // no real alternative for now.
387
    try
388
    {
389
        std::ofstream tmpfile;
390
        tmpfile.open(Str, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
391
 
392
        if (!tmpfile.is_open())
393
        {
394
            MSG_ERROR("Error opening a temporary file!");
395
        }
396
        else
397
            tmpfile.flush();
398
 
399
        tmpfile.close();
400
    }
401
    catch (std::exception& e)
402
    {
403
        MSG_ERROR("Couldn't create a temporary file: " << e.what());
404
        return string();
405
    }
406
 
407
    return Str;
408
}
409
 
115 andreas 410
/**
411
 * This methods checks if there exists a previous downloaded TP4 file. If this
412
 * is the case, nothing happens.
413
 * If there is no previous downloaded file it checks if there is one on the
414
 * controller and downloads it if it exists. After successfull download the
415
 * file is unpacked.
416
 *
417
 * @return TRUE is returned when there was a file successfully downloaded and
418
 * unpacked. In any other case FALSE is returned.
419
 */
420
bool TTPInit::loadSurfaceFromController(bool force)
421
{
422
    DECL_TRACER("TTPInit::loadSurfaceFromController(bool force)");
423
 
424
    TFsfReader reader;
120 andreas 425
    reader.regCallbackProgress(bind(&TTPInit::progressCallback, this, std::placeholders::_1));
115 andreas 426
    string target = mPath + "/" + TConfig::getFtpSurface();
117 andreas 427
    size_t pos = 0;
115 andreas 428
 
117 andreas 429
    if ((pos = mPath.find_last_of("/")) != string::npos)
430
        target = mPath.substr(0, pos) + "/" + TConfig::getFtpSurface();
431
 
115 andreas 432
    if (!force)
433
    {
122 andreas 434
        if (!isVirgin() || TConfig::getFtpDownloadTime() > 0)
115 andreas 435
            return false;
436
    }
437
 
120 andreas 438
    if (_processEvents)
439
        _processEvents();
440
 
119 andreas 441
    // To be sure the target directory tree is empty, we delete all files but
442
    // keep the system directories and their content, if they exist.
443
    dir::TDirectory dir;
444
 
445
    if (dir.exists(mPath))
446
    {
447
        dir.dropDir(mPath);
448
        dir.dropDir(mPath + "/fonts");
449
        dir.dropDir(mPath + "/images");
450
        dir.dropDir(mPath + "/sounds");
451
    }
452
 
115 andreas 453
    if (!reader.copyOverFTP(TConfig::getFtpSurface(), target))
126 andreas 454
    {
455
        if (TConfig::getFtpDownloadTime() == 0)
456
            createPanelConfigs();
457
 
115 andreas 458
        return false;
126 andreas 459
    }
115 andreas 460
 
120 andreas 461
    if (_processEvents)
462
        _processEvents();
463
 
117 andreas 464
    if (!reader.unpack(target, mPath))
465
    {
466
        MSG_ERROR("Unpacking was not successfull.");
115 andreas 467
        return false;
117 andreas 468
    }
115 andreas 469
 
119 andreas 470
    if (!force || !dir.exists(mPath + "/__system"))
118 andreas 471
    {
472
        createDirectoryStructure();
473
        createSystemConfigs();
474
    }
475
 
120 andreas 476
    if (_processEvents)
477
        _processEvents();
478
 
119 andreas 479
    dir.dropFile(target);       // We remove our traces
122 andreas 480
    dir.dropFile(mPath + SYSTEM_DEFAULT);   // No more system default files
115 andreas 481
    TConfig::saveFtpDownloadTime(time(NULL));
482
    TConfig::saveSettings();
483
    return true;
484
}
485
 
122 andreas 486
vector<string>& TTPInit::getFileList(const string& filter)
487
{
488
    DECL_TRACER("TTPInit::getFileList(const string& filter)");
489
 
490
    ftplib *ftp = new ftplib();
137 andreas 491
    ftp->regLogging(bind(&TTPInit::logging, this, std::placeholders::_1, std::placeholders::_2));
122 andreas 492
 
493
    if (TConfig::getFtpPassive())
494
        ftp->SetConnmode(ftplib::pasv);
495
    else
496
        ftp->SetConnmode(ftplib::port);
497
 
498
    string scon = TConfig::getController() + ":21";
499
    MSG_DEBUG("Trying to connect to " << scon);
500
 
501
    if (!ftp->Connect(scon.c_str()))
502
    {
503
        delete ftp;
504
        return mDirList;
505
    }
506
 
507
    string sUser = TConfig::getFtpUser();
508
    string sPass = TConfig::getFtpPassword();
509
    MSG_DEBUG("Trying to login <" << sUser << ", " << sPass << ">");
510
 
511
    if (!ftp->Login(sUser.c_str(), sPass.c_str()))
512
    {
513
        delete ftp;
514
        return mDirList;
515
    }
516
 
137 andreas 517
//    string tmpFile = std::tmpnam(nullptr);
518
    string tmpFile = getTmpFileName();
122 andreas 519
    MSG_DEBUG("Reading remote directory / into file " << tmpFile);
520
    ftp->Nlst(tmpFile.c_str(), "/");
521
    ftp->Quit();
522
    delete ftp;
523
    mDirList.clear();
524
 
525
    try
526
    {
527
        char buffer[1024];
528
        string uFilter = toUpper((std::string&)filter);
529
 
530
        std::ifstream ifile(tmpFile);
531
 
532
        while (ifile.getline(buffer, sizeof(buffer)))
533
        {
534
            string buf = buffer;
535
            string fname = trim(buf);
137 andreas 536
            MSG_DEBUG("FTP line: " << buf << " (" << fname << ")");
122 andreas 537
 
538
            if (!filter.empty())
539
            {
540
                if (endsWith(toUpper(buf), uFilter))
541
                    mDirList.push_back(trim(fname));
542
            }
543
            else
544
                mDirList.push_back(trim(fname));
545
        }
546
 
547
        ifile.close();
548
    }
549
    catch (std::exception& e)
550
    {
551
        MSG_ERROR("Error opening file " << tmpFile << ": " << e.what());
552
    }
553
 
554
    fs::remove(tmpFile);
555
 
556
    if (mDirList.size() > 0)
557
    {
558
        vector<string>::iterator iter;
559
 
560
        for (iter = mDirList.begin(); iter != mDirList.end(); ++iter)
561
        {
562
            MSG_DEBUG("File: " << *iter);
563
        }
564
    }
565
 
566
    return mDirList;
567
}
568
 
120 andreas 569
int TTPInit::progressCallback(off64_t xfer)
570
{
571
    DECL_TRACER("TTPInit::progressCallback(off64_t xfer)");
572
 
573
    if (_processEvents && xfer > 0)
574
        _processEvents();
575
 
576
    return 1;
577
}
578
 
122 andreas 579
bool TTPInit::isSystemDefault()
580
{
581
    DECL_TRACER("TTPInit::isSystemDefault()");
582
 
583
    try
584
    {
585
        string marker = mPath + SYSTEM_DEFAULT;
586
        return fs::exists(marker);
587
    }
588
    catch (std::exception& e)
589
    {
590
        MSG_ERROR("File system error: " << e.what())
591
        return false;
592
    }
593
 
594
    return true;
595
}
596
 
597
bool TTPInit::isVirgin()
598
{
599
    DECL_TRACER("TTPInit::isVirgin()");
600
 
601
    try
602
    {
603
        if (!fs::exists(mPath) || isSystemDefault())
604
            return true;
605
    }
606
    catch (std::exception& e)
607
    {
608
        MSG_ERROR("File system error: " << e.what());
609
        return true;
610
    }
611
 
612
    return false;
613
}
614
 
137 andreas 615
void TTPInit::logging(int level, const std::string &msg)
616
{
617
    switch(level)
618
    {
619
        case LOG_INFO:      MSG_INFO(msg); break;
620
        case LOG_WARNING:   MSG_WARNING(msg); break;
621
        case LOG_ERROR:     MSG_ERROR(msg); break;
622
        case LOG_TRACE:     MSG_TRACE(msg); break;
623
        case LOG_DEBUG:     MSG_DEBUG(msg); break;
624
    }
625
}
626
 
113 andreas 627
#ifdef __ANDROID__
43 andreas 628
bool TTPInit::askPermissions()
629
{
115 andreas 630
    DECL_TRACER("TTPInit::askPermissions()");
43 andreas 631
 
632
    QStringList permissions = { "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" };
633
    QtAndroid::PermissionResultMap perms = QtAndroid::requestPermissionsSync(permissions);
634
 
635
    for (auto iter = perms.begin(); iter != perms.end(); ++iter)
636
    {
637
        if (iter.value() == QtAndroid::PermissionResult::Denied)
638
            return false;
639
    }
640
 
641
    return true;
642
}
643
#endif