Completed
Push — master ( 2df547...733f1c )
by John
01:24
created

make_info()   C

Complexity

Conditions 7

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 7
dl 0
loc 36
rs 5.5
c 1
b 1
f 1
1
#!/usr/bin/env python3
2
"""This module contains various utilities for the scripts folder."""
3
4
import os  # path work
5
import getpass  # invisible password
6
import argparse  # generic parser
7
import sys  # getattr
8
import shutil  # folder removal
9
import glob  # file lookup
10
from bbarchivist import utilities  # little things
11
from bbarchivist import barutils  # file system work
12
from bbarchivist import archiveutils  # archive support
13
from bbarchivist import bbconstants  # constants
14
from bbarchivist import hashutils  # gpg
15
from bbarchivist import networkutils  # network tools
16
from bbarchivist import textgenerator  # writing text to file
17
from bbarchivist import smtputils  # email
18
from bbarchivist import sqlutils  # sql
19
20
__author__ = "Thurask"
21
__license__ = "WTFPL v2"
22
__copyright__ = "Copyright 2015-2016 Thurask"
23
24
25
def shortversion():
26
    """
27
    Get short app version (Git tag).
28
    """
29
    if not getattr(sys, 'frozen', False):
30
        ver = bbconstants.VERSION
31
    else:
32
        verfile = glob.glob(os.path.join(os.getcwd(), "version.txt"))[0]
33
        with open(verfile) as afile:
34
            ver = afile.read()
35
    return ver
36
37
38
def longversion():
39
    """
40
    Get long app version (Git tag + commits + hash).
41
    """
42
    if not getattr(sys, 'frozen', False):
43
        ver = (bbconstants.LONGVERSION, bbconstants.COMMITDATE)
44
    else:
45
        verfile = glob.glob(os.path.join(os.getcwd(), "longversion.txt"))[0]
46
        with open(verfile) as afile:
47
            ver = afile.read().split("\n")
48
    return ver
49
50
51
def default_parser(name=None, desc=None, flags=None, vers=None):
52
    """
53
    A generic form of argparse's ArgumentParser.
54
55
    :param name: App name.
56
    :type name: str
57
58
    :param desc: App description.
59
    :type desc: str
60
61
    :param flags: Tuple of sections to add.
62
    :type flags: tuple(str)
63
64
    :param vers: Versions: [git commit hash, git commit date]
65
    :param vers: list(str)
66
    """
67
    if vers is None:
68
        vers = longversion()
69
    parser = argparse.ArgumentParser(
70
        prog=name,
71
        description=desc,
72
        epilog="https://github.com/thurask/bbarchivist")
73
    parser.add_argument(
74
        "-v",
75
        "--version",
76
        action="version",
77
        version="{0} {1} committed {2}".format(parser.prog, vers[0], vers[1]))
78
    if flags is not None:
79
        if "folder" in flags:
80
            parser.add_argument(
81
                "-f",
82
                "--folder",
83
                dest="folder",
84
                help="Working folder",
85
                default=None,
86
                metavar="DIR",
87
                type=utilities.file_exists)
88
        if "osr" in flags:
89
            parser.add_argument(
90
                "os",
91
                help="OS version")
92
            parser.add_argument(
93
                "radio",
94
                help="Radio version, 10.x.y.zzzz",
95
                nargs="?",
96
                default=None)
97
            parser.add_argument(
98
                "swrelease",
99
                help="Software version, 10.x.y.zzzz",
100
                nargs="?",
101
                default=None)
102
    return parser
103
104
105
def external_version(parser, addition):
106
    """
107
    Modify the version string of argparse.ArgumentParser, adding something.
108
109
    :param parser: Parser to modify.
110
    :type parser: argparse.ArgumentParser
111
112
    :param addition: What to add.
113
    :type addition: str
114 View Code Duplication
    """
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
115
    verarg = [arg for arg in parser._actions if type(arg) == argparse._VersionAction][0]
116
    verarg.version = "{1}{0}".format(addition, verarg.version)
117
    return parser
118
119
120
def return_radio_version(osversion, radioversion=None):
121
    """
122
    Increment radio version, if need be.
123
124
    :param osversion: OS version.
125
    :type osversion: str
126
127
    :param radioversion: Radio version, None if incremented.
128
    :type radioversion: str
129
    """
130
    if radioversion is None:
131
        radioversion = utilities.increment(osversion, 1)
132
    return radioversion
133
134
135
def return_sw_checked(softwareversion, osversion):
136
    """
137
    Check software existence, return boolean.
138
139
    :param softwareversion: Software release version.
140
    :type softwareversion: str
141
142
    :param osversion: OS version.
143 View Code Duplication
    :type osversion: str
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
144
    """
145
    if softwareversion is None:
146
        serv = bbconstants.SERVERS["p"]
147
        softwareversion = networkutils.sr_lookup(osversion, serv)
148
        if softwareversion == "SR not in system":
149
            print("SOFTWARE RELEASE NOT FOUND")
150
            cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
151
            if cont:
152
                softwareversion = input("SOFTWARE RELEASE: ")
153
                swchecked = False
154
            else:
155
                print("\nEXITING...")
156
                raise SystemExit  # bye bye
157
        else:
158
            swchecked = True
159
    else:
160
        swchecked = True
161
    return softwareversion, swchecked
162
163
164
def return_radio_sw_checked(altsw, radioversion):
165
    """
166
    Check radio software existence, return boolean.
167
168
    :param altsw: Software release version.
169
    :type altsw: str
170
171
    :param radioversion: Radio version.
172
    :type radioversion: str
173
    """
174
    if altsw == "checkme":
175
        serv = bbconstants.SERVERS["p"]
176
        testos = utilities.increment(radioversion, -1)
177
        altsw = networkutils.sr_lookup(testos, serv)
178
        if altsw == "SR not in system":
179
            print("RADIO SOFTWARE RELEASE NOT FOUND")
180
            cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
181
            if cont:
182
                altsw = input("SOFTWARE RELEASE: ")
183
                altchecked = False
184
            else:
185
                print("\nEXITING...")
186
                raise SystemExit  # bye bye
187
        else:
188
            altchecked = True
189
    else:
190
        altchecked = True
191
    return altsw, altchecked
192
193
194
def check_sw(baseurl, softwareversion, swchecked):
195
    """
196
    Check existence of software release.
197
198
    :param baseurl: Base URL (from http to hashed SW release).
199
    :type basurl: str
200
201
    :param softwareversion: Software release.
202
    :type softwareversion: str
203
204
    :param swchecked: If we checked the sw release already.
205
    :type swchecked: bool
206
    """
207
    print("CHECKING SOFTWARE RELEASE AVAILABILITY...")
208
    if not swchecked:
209
        avlty = networkutils.availability(baseurl)
210
        if avlty:
211
            print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
212
        else:
213
            print("SOFTWARE RELEASE {0} NOT FOUND".format(softwareversion))
214
            cont = utilities.s2b(input("CONTINUE? Y/N: "))
215
            if not cont:
216
                print("\nEXITING...")
217
                raise SystemExit
218
    else:
219
        print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
220
221
222
def check_radio_sw(alturl, altsw, altchecked):
223
    """
224
    Check existence of radio software release.
225
226
    :param alturl: Radio base URL (from http to hashed SW release).
227
    :type alturl: str
228
229
    :param altsw: Radio software release.
230
    :type altsw: str
231
232
    :param altchecked: If we checked the sw release already.
233
    :type altchecked: bool
234
    """
235
    print("CHECKING RADIO SOFTWARE RELEASE...")
236
    if not altchecked:
237
        altavlty = networkutils.availability(alturl)
238
        if altavlty:
239
            print("SOFTWARE RELEASE {0} EXISTS".format(altsw))
240
        else:
241
            print("SOFTWARE RELEASE {0} NOT FOUND".format(altsw))
242
            cont = utilities.s2b(input("CONTINUE? Y/N: "))
243
            if not cont:
244
                print("\nEXITING...")
245
                raise SystemExit
246
    else:
247
        print("SOFTWARE RELEASE {0} EXISTS".format(altsw))
248
249
250
def check_os_single(osurl, osversion, device):
251
    """
252
    Check existence of single OS link.
253
254
    :param radiourl: Radio URL to check.
255
    :type radiourl: str
256
257
    :param radioversion: Radio version.
258
    :type radioversion: str
259
260
    :param device: Device family.
261
    :type device: int
262
    """
263
    osav = networkutils.availability(osurl)
264
    if not osav:
265
        print("{0} NOT AVAILABLE FOR {1}".format(osversion, bbconstants.DEVICES[device]))
266
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
267
        if not cont:
268
            print("\nEXITING...")
269
            raise SystemExit
270
271
272
def check_os_bulk(osurls):
273
    """
274
    Check existence of list of OS links.
275
276
    :param osurls: OS URLs to check.
277
    :type osurls: list(str)
278
    """
279
    for url in osurls:
280
        osav = networkutils.availability(url)
281
        if osav:
282
            break
283
    else:
284
        print("OS VERSION NOT FOUND")
285
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
286
        if not cont:
287
            print("\nEXITING...")
288
            raise SystemExit
289
290
291
def check_radio_single(radiourl, radioversion):
292
    """
293
    Check existence of single radio link.
294
295
    :param radiourl: Radio URL to check.
296
    :type radiourl: str
297
298
    :param radioversion: Radio version.
299
    :type radioversion: str
300
    """
301
    radav = networkutils.availability(radiourl)
302
    if not radav:
303
        print("RADIO VERSION NOT FOUND")
304
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
305
        if cont:
306
            rad2 = input("RADIO VERSION: ")
307
            radiourl = radiourl.replace(radioversion, rad2)
308
            radioversion = rad2
309
        else:
310
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
311
            if not going:
312
                print("\nEXITING...")
313
                raise SystemExit
314
    return radiourl, radioversion
315
316
317
def check_radio_bulk(radiourls, radioversion):
318
    """
319
    Check existence of list of radio links.
320
321
    :param radiourls: Radio URLs to check.
322
    :type radiourls: list(str)
323
324
    :param radioversion: Radio version.
325
    :type radioversion: str
326
    """
327
    for url in radiourls:
328
        radav = networkutils.availability(url)
329
        if radav:
330
            break
331
    else:
332
        print("RADIO VERSION NOT FOUND")
333
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
334
        if cont:
335
            rad2 = input("RADIO VERSION: ")
336
            radiourls = [url.replace(radioversion, rad2) for url in radiourls]
337
            radioversion = rad2
338
        else:
339
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
340
            if not going:
341
                print("\nEXITING...")
342
                raise SystemExit
343
    return radiourls, radioversion
344
345
346
def get_baseurls(softwareversion, altsw=None):
347
    """
348
    Generate base URLs for bar links.
349
350
    :param softwareversion: Software version.
351
    :type softwareversion: str
352
353
    :param altsw: Radio software version, if necessary.
354
    :type altsw: str
355
    """
356
    baseurl = networkutils.create_base_url(softwareversion)
357
    alturl = networkutils.create_base_url(altsw) if altsw else None
358
    return baseurl, alturl
359
360
361
def get_sz_executable(compmethod):
362
    """
363
    Get 7z executable.
364
365
    :param compmethod: Compression method.
366
    :type compmethod: str
367
    """
368
    if compmethod != "7z":
369
        szexe = ""
370
    else:
371
        print("CHECKING PRESENCE OF 7ZIP...")
372
        psz = utilities.prep_seven_zip(True)
373
        if psz:
374
            print("7ZIP OK")
375
            szexe = utilities.get_seven_zip(False)
376
        else:
377
            szexe = ""
378
            print("7ZIP NOT FOUND")
379
            cont = utilities.s2b(input("CONTINUE? Y/N "))
380
            if cont:
381
                print("FALLING BACK TO ZIP...")
382
                compmethod = "zip"
383
            else:
384
                print("\nEXITING...")
385
                raise SystemExit  # bye bye
386
    return compmethod, szexe
387
388
389
def test_bar_files(localdir, urllist):
390
    """
391
    Test bar files after download.
392
393
    :param localdir: Directory.
394
    :type localdir: str
395
396
    :param urllist: List of URLs to check.
397
    :type urllist: list(str)
398
    """
399
    brokenlist = []
400
    print("TESTING BAR FILES...")
401
    for file in os.listdir(localdir):
402
        if file.endswith(".bar"):
403
            print("TESTING: {0}".format(file))
404
            thepath = os.path.abspath(os.path.join(localdir, file))
405
            brokens = barutils.bar_tester(thepath)
406
            if brokens is not None:
407
                os.remove(brokens)
408
                for url in urllist:
409
                    if brokens in url:
410
                        brokenlist.append(url)
411
    if brokenlist:
412
        print("SOME FILES ARE BROKEN!")
413
        utilities.lprint(brokenlist)
414
        raise SystemExit
415
    else:
416
        print("BAR FILES DOWNLOADED OK")
417
418
419
def test_signed_files(localdir):
420
    """
421
    Test signed files after extract.
422
423
    :param localdir: Directory.
424
    :type localdir: str
425
    """
426
    print("TESTING SIGNED FILES...")
427
    for file in os.listdir(localdir):
428
        if file.endswith(".bar"):
429
            print("TESTING: {0}".format(file))
430
            signname, signhash = barutils.retrieve_sha512(file)
431
            sha512ver = barutils.verify_sha512(signname, signhash)
432
            if not sha512ver:
433
                print("{0} IS BROKEN".format((file)))
434
                break
435
    else:
436
        print("ALL FILES EXTRACTED OK")
437
438
439
def test_loader_files(localdir):
440
    """
441
    Test loader files after creation.
442
443
    :param localdir: Directory.
444
    :type localdir: str
445
    """
446
    if not utilities.is_windows():
447
        pass
448
    else:
449
        print("TESTING LOADER FILES...")
450
        brokens = utilities.verify_bulk_loaders(localdir)
451
        if brokens:
452
            print("BROKEN FILES:")
453
            utilities.lprint(brokens)
454
            raise SystemExit
455
        else:
456
            print("ALL FILES CREATED OK")
457
458
459
def test_single_loader(loaderfile):
460
    """
461
    Test single loader file after creation.
462
463
    :param loaderfile: File to check.
464
    :type loaderfile: str
465
    """
466
    if not utilities.is_windows():
467
        pass
468
    else:
469
        print("TESTING LOADER...")
470
        if not utilities.verify_loader_integrity(loaderfile):
471
            print("{0} IS BROKEN!".format(os.path.basename(loaderfile)))
472
            raise SystemExit
473
        else:
474
            print("LOADER CREATED OK")
475
476
477
def prod_avail(results, mailer=False, osversion=None, password=None):
478
    """
479
    Clean availability for production lookups for autolookup script.
480
481
    :param results: Result dict.
482
    :type results: dict(str: str)
483
484
    :param mailer: If we're mailing links. Default is false.
485
    :type mailer: bool
486
487
    :param osversion: OS version.
488
    :type osversion: str
489
490
    :param password: Email password.
491
    :type password: str
492
    """
493
    prel = results['p']
494
    if prel != "SR not in system" and prel is not None:
495
        pav = "PD"
496
        baseurl = networkutils.create_base_url(prel)
497
        avail = networkutils.availability(baseurl)
498
        is_avail = "Available" if avail else "Unavailable"
499
        if avail and mailer:
500
            sqlutils.prepare_sw_db()
501
            if not sqlutils.check_exists(osversion, prel):
502
                rad = utilities.increment(osversion, 1)
503
                linkgen(osversion, rad, prel, temp=True)
504
                smtputils.prep_email(osversion, prel, password)
505
    else:
506
        pav = "  "
507
        is_avail = "Unavailable"
508
    return prel, pav, is_avail
509
510
511
def linkgen(osversion, radioversion=None, softwareversion=None, altsw=None, temp=False, sdk=False):
512
    """
513
    Generate debrick/core/radio links for given OS, radio, software release.
514
515
    :param osversion: OS version, 10.x.y.zzzz.
516
    :type osversion: str
517
518
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
519
    :type radioversion: str
520
521
    :param softwareversion: Software version, 10.x.y.zzzz. Can be guessed.
522
    :type softwareversion: str
523
524
    :param altsw: Radio software release, if not the same as OS.
525
    :type altsw: str
526
527
    :param temp: If file we write to is temporary. Default is False.
528
    :type temp: bool
529
530
    :param sdk: If we specifically want SDK images. Default is False.
531
    :type sdk: bool
532
    """
533
    radioversion = return_radio_version(osversion, radioversion)
534
    softwareversion, swc = return_sw_checked(softwareversion, osversion)
535
    del swc
536
    if altsw is not None:
537
        altsw, aswc = return_radio_sw_checked(altsw, radioversion)
538
        del aswc
539
    baseurl = networkutils.create_base_url(softwareversion)
540
    oses, cores, radios = textgenerator.url_gen(osversion, radioversion, softwareversion)
541
    if altsw is not None:
542
        del radios
543
        dbks, cors, radios = textgenerator.url_gen(osversion, radioversion, altsw)
544
        del dbks
545
        del cors
546
    avlty = networkutils.availability(baseurl)
547
    if sdk:
548
        oses2 = {key: val.replace("factory_sfi", "sdk") for key, val in oses.items()}
549
        cores2 = {key: val.replace("factory_sfi", "sdk") for key, val in cores.items()}
550
        oses = {key: val.replace("verizon_sfi", "sdk") for key, val in oses2.items()}
551
        cores = {key: val.replace("verizon_sfi", "sdk") for key, val in cores2.items()}
552
    textgenerator.write_links(softwareversion, osversion, radioversion, oses, cores, radios,
553
                              avlty, False, None, temp, altsw)
554
555
556
def clean_swrel(swrelset):
557
    """
558
    Clean a list of software release lookups.
559
560
    :param swrelset: List of software releases.
561
    :type swrelset: set(str)
562
    """
563
    for i in swrelset:
564
        if i != "SR not in system" and i is not None:
565
            swrelease = i
566
            break
567
    else:
568
        swrelease = ""
569
    return swrelease
570
571
572
def autolookup_printer(out, avail, log=False, quiet=False, record=None):
573
    """
574
    Print autolookup results, logging if specified.
575
576
    :param out: Output block.
577
    :type out: str
578
579
    :param avail: Availability. Can be "Available" or "Unavailable".
580
    :type avail: str
581
582
    :param log: If we're logging to file.
583
    :type log: bool
584
585
    :param quiet: If we only note available entries.
586
    :type quiet: bool
587
588
    :param record: If we're logging, the file to log to.
589
    :type record: str
590
    """
591
    if not quiet:
592
        avail = "Available"  # force things
593
    if avail == "Available":
594
        if log:
595
            with open(record, "a") as rec:
596
                rec.write("{0}\n".format(out))
597
        print(out)
598
599
600
def autolookup_output(osversion, swrelease, avail, avpack, sql=False):
601
    """
602
    Prepare autolookup block, and add to SQL database.
603
604
    :param osversion: OS version.
605
    :type osversion: str
606
607
    :param swrelease: Software release.
608
    :type swrelease: str
609
610
    :param avail: "Unavailable" or "Available".
611
    :type avail: str
612
613
    :param avpack: Availabilities: alpha 1 and 2, beta 1 and 2, production.
614
    :type avpack: list(str)
615
616
    :param sql: If we're adding this to our SQL database.
617
    :type sql: bool
618
    """
619
    if sql:
620
        sqlutils.prepare_sw_db()
621
        if not sqlutils.check_exists(osversion, swrelease):
622
            sqlutils.insert(osversion, swrelease, avail.lower())
623
    avblok = "[{0}|{1}|{2}|{3}|{4}]".format(*avpack)
1 ignored issue
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
624
    out = "OS {0} - SR {1} - {2} - {3}".format(osversion, swrelease, avblok, avail)
625
    return out
626
627
628
def export_cchecker(files, npc, hwid, osv, radv, swv, upgrade=False, forced=None):
629
    """
630
    Write carrierchecker lookup links to file.
631
632
    :param files: List of file URLs.
633
    :type files: list(str)
634
635
    :param npc: MCC + MNC (ex. 302220).
636
    :type npc: int
637
638
    :param hwid: Device hardware ID.
639
    :type hwid: str
640
641
    :param osv: OS version.
642
    :type osv: str
643
644
    :param radv: Radio version.
645
    :type radv: str
646
647
    :param swv: Software release.
648
    :type swv: str
649
650
    :param upgrade: Whether or not to use upgrade files. Default is false.
651
    :type upgrade: bool
652
653
    :param forced: Force a software release. None to go for latest.
654
    :type forced: str
655
    """
656
    if files:
657
        if not upgrade:
658
            newfiles = networkutils.carrier_query(npc, hwid, True, False, forced)
659
            cleanfiles = newfiles[3]
660
        else:
661
            cleanfiles = files
662
        osurls, coreurls, radiourls = textgenerator.url_gen(osv, radv, swv)
663
        stoppers = ["8960", "8930", "8974", "m5730", "winchester"]
664
        finals = [link for link in cleanfiles if all(word not in link for word in stoppers)]
665
        textgenerator.write_links(swv, osv, radv, osurls, coreurls, radiourls, True, True, finals)
666
        print("\nFINISHED!!!")
667
    else:
668
        print("CANNOT EXPORT, NO SOFTWARE RELEASE")
669
670
671
def generate_blitz_links(files, osv, radv, swv):
672
    """
673
    Generate blitz URLs (i.e. all OS and radio links).
674
    :param files: List of file URLs.
675
    :type files: list(str)
676
677
    :param osv: OS version.
678
    :type osv: str
679
680
    :param radv: Radio version.
681
    :type radv: str
682
683
    :param swv: Software release.
684
    :type swv: str
685
    """
686
    baseurl = networkutils.create_base_url(swv)
687
    suffix = "nto+armle-v7+signed.bar"
688
    coreurls = [
689
        "{0}/winchester.factory_sfi-{1}-{2}".format(baseurl, osv, suffix),
690
        "{0}/qc8960.factory_sfi-{1}-{2}".format(baseurl, osv, suffix),
691
        "{0}/qc8960.factory_sfi_hybrid_qc8x30-{1}-{2}".format(baseurl, osv, suffix),
692
        "{0}/qc8974.factory_sfi_hybrid_qc8974-{1}-{2}".format(baseurl, osv, suffix)
693
    ]
694
    radiourls = [
695
        "{0}/m5730-{1}-{2}".format(baseurl, radv, suffix),
696
        "{0}/qc8960-{1}-{2}".format(baseurl, radv, suffix),
697
        "{0}/qc8960.wtr-{1}-{2}".format(baseurl, radv, suffix),
698
        "{0}/qc8960.wtr5-{1}-{2}".format(baseurl, radv, suffix),
699
        "{0}/qc8930.wtr5-{1}-{2}".format(baseurl, radv, suffix),
700
        "{0}/qc8974.wtr2-{1}-{2}".format(baseurl, radv, suffix)
701
    ]
702
    return files + coreurls + radiourls
703
704
705
def package_blitz(bardir, swv):
706
    """
707
    Package and verify a blitz package.
708
709
    :param bardir: Path to folder containing bar files.
710
    :type bardir: str
711
712
    :param swv: Software version.
713
    :type swv: str
714
    """
715
    print("\nCREATING BLITZ...")
716
    barutils.create_blitz(bardir, swv)
717
    print("\nTESTING BLITZ...")
718
    zipver = archiveutils.zip_verify("Blitz-{0}.zip".format(swv))
719
    if not zipver:
720
        print("BLITZ FILE IS BROKEN")
721
        raise SystemExit
722
    else:
723
        shutil.rmtree(bardir)
724
725
726
def purge_dross(files, craplist):
727
    """
728
    Get rid of Nuance/retaildemo apps in a list of apps.
729
730
    :param files: List of URLs to clean.
731
    :type files: list(str)
732
733
    :param craplist: List of fragments to check for and remove.
734
    :type craplist: list(str)
735
    """
736
    files2 = [file for file in files if all(word not in file for word in craplist)]
737
    return files2
738
739
740
def slim_preamble(appname):
741
    """
742
    Standard app name header.
743
744
    :param appname: Name of app.
745
    :type appname: str
746
    """
747
    print("~~~{0} VERSION {1}~~~".format(appname.upper(), shortversion()))
748
749
750
def standard_preamble(appname, osversion, softwareversion, radioversion, altsw=None):
751
    """
752
    Standard app name, OS, radio and software (plus optional radio software) print block.
753
754
    :param appname: Name of app.
755
    :type appname: str
756
757
    :param osversion: OS version, 10.x.y.zzzz. Required.
758
    :type osversion: str
759
760
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
761
    :type radioversion: str
762
763
    :param softwareversion: Software release, 10.x.y.zzzz. Can be guessed.
764
    :type softwareversion: str
765
766
    :param altsw: Radio software release, if not the same as OS.
767
    :type altsw: str
768
    """
769
    slim_preamble(appname)
770
    print("OS VERSION: {0}".format(osversion))
771
    print("OS SOFTWARE VERSION: {0}".format(softwareversion))
772
    print("RADIO VERSION: {0}".format(radioversion))
773
    if altsw is not None:
774
        print("RADIO SOFTWARE VERSION: {0}".format(altsw))
775
776
777
def verify_gpg_credentials():
778
    """
779
    Read GPG key/pass from file, verify if incomplete.
780
    """
781
    gpgkey, gpgpass = hashutils.gpg_config_loader()
782
    if gpgkey is None or gpgpass is None:
783
        print("NO PGP KEY/PASS FOUND")
784
        cont = utilities.s2b(input("CONTINUE (Y/N)?: "))
785
        if cont:
786
            if gpgkey is None:
787
                gpgkey = input("PGP KEY (0x12345678): ")
788
                if not gpgkey.startswith("0x"):
789
                    gpgkey = "0x{0}".format(gpgkey)   # add preceding 0x
790
            if gpgpass is None:
791
                gpgpass = getpass.getpass(prompt="PGP PASSPHRASE: ")
792
                writebool = utilities.s2b(input("SAVE PASSPHRASE (Y/N)?:"))
793
            else:
794
                writebool = False
795
            gpgpass2 = gpgpass if writebool else None
796
            hashutils.gpg_config_writer(gpgkey, gpgpass2)
797
        else:
798
            gpgkey = None
799
    return gpgkey, gpgpass
800
801
802
def bulk_hash(dirs, compressed=True, deleted=True, radios=True, hashdict=None):
803
    """
804
    Hash files in several folders based on flags.
805
806
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
807
    :type dirs: list(str)
808
809
    :param compressed: Whether to hash compressed files. True by default.
810
    :type compressed: bool
811
812
    :param deleted: Whether to delete uncompressed files. True by default.
813
    :type deleted: bool
814
815
    :param radios: Whether to hash radio autoloaders. True by default.
816
    :type radios: bool
817
818
    :param hashdict: Dictionary of hash rules, in ~\bbarchivist.ini.
819
    :type hashdict: dict({str: bool})
820
    """
821
    print("HASHING LOADERS...")
822
    if compressed:
823
        hashutils.verifier(dirs[4], hashdict)
824
        if radios:
825
            hashutils.verifier(dirs[5], hashdict)
826
    if not deleted:
827
        hashutils.verifier(dirs[2], hashdict)
828
        if radios:
829
            hashutils.verifier(dirs[3], hashdict)
830
831
832
def bulk_verify(dirs, compressed=True, deleted=True, radios=True):
833
    """
834
    Verify files in several folders based on flags.
835
836
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
837
    :type dirs: list(str)
838
839
    :param compressed: Whether to hash compressed files. True by default.
840
    :type compressed: bool
841
842
    :param deleted: Whether to delete uncompressed files. True by default.
843
    :type deleted: bool
844
845
    :param radios: Whether to hash radio autoloaders. True by default.
846
    :type radios: bool
847
    """
848
    gpgkey, gpgpass = verify_gpg_credentials()
849
    if gpgpass is not None and gpgkey is not None:
850
        print("VERIFYING LOADERS...")
851
        print("KEY: {0}".format(gpgkey))
852
        if compressed:
853
            hashutils.gpgrunner(dirs[4], gpgkey, gpgpass, True)
854
            if radios:
855
                hashutils.gpgrunner(dirs[5], gpgkey, gpgpass, True)
856
        if not deleted:
857
            hashutils.gpgrunner(dirs[2], gpgkey, gpgpass, True)
858
            if radios:
859
                hashutils.gpgrunner(dirs[3], gpgkey, gpgpass, True)
860
861
862
def enn_ayy(quant):
863
    """
864
    Cheeky way to put a N/A placeholder for a string.
865
866
    :param quant: What to check if it's None.
867
    :type quant: str
868
    """
869
    return "N/A" if quant is None else quant
870
871
872
def info_header(afile, osver, radio=None, software=None, device=None):
873
    """
874
    Write header for info file.
875
876
    :param afile: Open file to write to.
877
    :type afile: File object
878
879
    :param osver: OS version, required for both types.
880
    :type osver: str
881
882
    :param radio: Radio version, required for QNX.
883
    :type radio: str
884
885
    :param software: Software release, required for QNX.
886
    :type software: str
887
888
    :param device: Device type, required for Android.
889
    :type device: str
890
    """
891
    afile.write("OS: {0}\n".format(osver))
892
    if device:
893
        afile.write("Device: {0}\n".format(enn_ayy(device)))
894
    else:
895
        afile.write("Radio: {0}\n".format(enn_ayy(radio)))
896
        afile.write("Software: {0}\n".format(enn_ayy(software)))
897
    afile.write("{0}\n".format("~"*40))
898
899
900
def make_info(filepath, osver, radio=None, software=None, device=None):
901
    """
902
    Create a new-style info (names, sizes and hashes) file.
903
904
    :param filepath: Path to folder to analyze.
905
    :type filepath: str
906
907
    :param osver: OS version, required for both types.
908
    :type osver: str
909
910
    :param radio: Radio version, required for QNX.
911
    :type radio: str
912
913
    :param software: Software release, required for QNX.
914
    :type software: str
915
916
    :param device: Device type, required for Android.
917
    :type device: str
918
    """
919
    fileext = ".zip" if device else ".7z"
920
    files = os.listdir(filepath)
921
    absfiles = [os.path.join(filepath, x) for x in files if x.endswith((fileext, ".exe"))]
922
    fname = os.path.join(filepath, "!{0}_OSINFO!.txt".format(osver))
923
    with open(fname, "w") as afile:
924
        info_header(afile, osver, radio, software, device)
925
        for indx, file in enumerate(absfiles):
926
            fsize = os.stat(file).st_size
927
            afile.write("File: {0}\n".format(os.path.basename(file)))
928
            afile.write("\tSize: {0} ({1})\n".format(fsize, utilities.fsizer(fsize)))
929
            afile.write("\tHashes:\n")
930
            afile.write("\t\tMD5: {0}\n".format(hashutils.hm5(file).upper()))
931
            afile.write("\t\tSHA1: {0}\n".format(hashutils.hs1(file).upper()))
932
            afile.write("\t\tSHA256: {0}\n".format(hashutils.hs256(file).upper()))
933
            afile.write("\t\tSHA512: {0}\n".format(hashutils.hs512(file).upper()))
934
            if indx != len(absfiles) - 1:
935
                afile.write("\n")
936
937
938
def bulk_info(dirs, osv, compressed=True, deleted=True, radios=True, rad=None, swv=None, dev=None):
939
    """
940
    Generate info files in several folders based on flags.
941
942
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
943
    :type dirs: list(str)
944
945
    :param osver: OS version, required for both types.
946
    :type osver: str
947
948
    :param compressed: Whether to hash compressed files. True by default.
949
    :type compressed: bool
950
951
    :param deleted: Whether to delete uncompressed files. True by default.
952
    :type deleted: bool
953
954
    :param radios: Whether to hash radio autoloaders. True by default.
955
    :type radios: bool
956
957
    :param rad: Radio version, required for QNX.
958
    :type rad: str
959
960
    :param swv: Software release, required for QNX.
961
    :type swv: str
962
963
    :param dev: Device type, required for Android.
964
    :type dev: str
965
    """
966
    print("GENERATING INFO FILES...")
967
    if compressed:
968
        make_info(dirs[4], osv, rad, swv, dev)
969
        if radios:
970
            make_info(dirs[5], osv, rad, swv, dev)
971
    if not deleted:
972
        make_info(dirs[2], osv, rad, swv, dev)
973
        if radios:
974
            make_info(dirs[3], osv, rad, swv, dev)
975