Completed
Push — master ( 5a1628...c6611a )
by John
01:19
created

autolookup_logger()   A

Complexity

Conditions 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 12
rs 9.4285
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
import _thread  # run stuff in background
11
from bbarchivist import utilities  # little things
12
from bbarchivist import barutils  # file system work
13
from bbarchivist import archiveutils  # archive support
14
from bbarchivist import bbconstants  # constants
15
from bbarchivist import hashutils  # gpg
16
from bbarchivist import networkutils  # network tools
17
from bbarchivist import textgenerator  # writing text to file
18
from bbarchivist import smtputils  # email
19
from bbarchivist import sqlutils  # sql
20
21
__author__ = "Thurask"
22
__license__ = "WTFPL v2"
23
__copyright__ = "Copyright 2015-2016 Thurask"
24
25
26
def shortversion():
27
    """
28
    Get short app version (Git tag).
29
    """
30
    if not getattr(sys, 'frozen', False):
31
        ver = bbconstants.VERSION
32
    else:
33
        verfile = glob.glob(os.path.join(os.getcwd(), "version.txt"))[0]
34
        with open(verfile) as afile:
35
            ver = afile.read()
36
    return ver
37
38
39
def longversion():
40
    """
41
    Get long app version (Git tag + commits + hash).
42
    """
43
    if not getattr(sys, 'frozen', False):
44
        ver = (bbconstants.LONGVERSION, bbconstants.COMMITDATE)
45
    else:
46
        verfile = glob.glob(os.path.join(os.getcwd(), "longversion.txt"))[0]
47
        with open(verfile) as afile:
48
            ver = afile.read().split("\n")
49
    return ver
50
51
52
def default_parser(name=None, desc=None, flags=None, vers=None):
53
    """
54
    A generic form of argparse's ArgumentParser.
55
56
    :param name: App name.
57
    :type name: str
58
59
    :param desc: App description.
60
    :type desc: str
61
62
    :param flags: Tuple of sections to add.
63
    :type flags: tuple(str)
64
65
    :param vers: Versions: [git commit hash, git commit date]
66
    :param vers: list(str)
67
    """
68
    if vers is None:
69
        vers = longversion()
70
    parser = argparse.ArgumentParser(
71
        prog=name,
72
        description=desc,
73
        epilog="https://github.com/thurask/bbarchivist")
74
    parser.add_argument(
75
        "-v",
76
        "--version",
77
        action="version",
78
        version="{0} {1} committed {2}".format(parser.prog, vers[0], vers[1]))
79
    if flags is not None:
80
        if "folder" in flags:
81
            parser.add_argument(
82
                "-f",
83
                "--folder",
84
                dest="folder",
85
                help="Working folder",
86
                default=None,
87
                metavar="DIR",
88
                type=utilities.file_exists)
89
        if "osr" in flags:
90
            parser.add_argument(
91
                "os",
92
                help="OS version")
93
            parser.add_argument(
94
                "radio",
95
                help="Radio version, 10.x.y.zzzz",
96
                nargs="?",
97
                default=None)
98
            parser.add_argument(
99
                "swrelease",
100
                help="Software version, 10.x.y.zzzz",
101
                nargs="?",
102
                default=None)
103
    return parser
104
105
106
def external_version(parser, addition):
107
    """
108
    Modify the version string of argparse.ArgumentParser, adding something.
109
110
    :param parser: Parser to modify.
111
    :type parser: argparse.ArgumentParser
112
113
    :param addition: What to add.
114 View Code Duplication
    :type addition: str
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
115
    """
116
    verarg = [arg for arg in parser._actions if isinstance(arg, argparse._VersionAction)][0]
117
    verarg.version = "{1}{0}".format(addition, verarg.version)
118
    return parser
119
120
121
def return_radio_version(osversion, radioversion=None):
122
    """
123
    Increment radio version, if need be.
124
125
    :param osversion: OS version.
126
    :type osversion: str
127
128
    :param radioversion: Radio version, None if incremented.
129
    :type radioversion: str
130
    """
131
    if radioversion is None:
132
        radioversion = utilities.increment(osversion, 1)
133
    return radioversion
134
135
136
def return_sw_checked(softwareversion, osversion):
137
    """
138
    Check software existence, return boolean.
139
140
    :param softwareversion: Software release version.
141
    :type softwareversion: str
142
143 View Code Duplication
    :param osversion: OS version.
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
144
    :type osversion: str
145
    """
146
    if softwareversion is None:
147
        serv = bbconstants.SERVERS["p"]
148
        softwareversion = networkutils.sr_lookup(osversion, serv)
149
        if softwareversion == "SR not in system":
150
            print("SOFTWARE RELEASE NOT FOUND")
151
            cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
152
            if cont:
153
                softwareversion = input("SOFTWARE RELEASE: ")
154
                swchecked = False
155
            else:
156
                print("\nEXITING...")
157
                raise SystemExit  # bye bye
158
        else:
159
            swchecked = True
160
    else:
161
        swchecked = True
162
    return softwareversion, swchecked
163
164
165
def return_radio_sw_checked(altsw, radioversion):
166
    """
167
    Check radio software existence, return boolean.
168
169
    :param altsw: Software release version.
170
    :type altsw: str
171
172
    :param radioversion: Radio version.
173
    :type radioversion: str
174
    """
175
    if altsw == "checkme":
176
        serv = bbconstants.SERVERS["p"]
177
        testos = utilities.increment(radioversion, -1)
178
        altsw = networkutils.sr_lookup(testos, serv)
179
        if altsw == "SR not in system":
180
            print("RADIO SOFTWARE RELEASE NOT FOUND")
181
            cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
182
            if cont:
183
                altsw = input("SOFTWARE RELEASE: ")
184
                altchecked = False
185
            else:
186
                print("\nEXITING...")
187
                raise SystemExit  # bye bye
188
        else:
189
            altchecked = True
190
    else:
191
        altchecked = True
192
    return altsw, altchecked
193
194
195
def check_sw(baseurl, softwareversion, swchecked):
196
    """
197
    Check existence of software release.
198
199
    :param baseurl: Base URL (from http to hashed SW release).
200
    :type basurl: str
201
202
    :param softwareversion: Software release.
203
    :type softwareversion: str
204
205
    :param swchecked: If we checked the sw release already.
206
    :type swchecked: bool
207
    """
208
    print("CHECKING SOFTWARE RELEASE AVAILABILITY...")
209
    if not swchecked:
210
        avlty = networkutils.availability(baseurl)
211
        if avlty:
212
            print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
213
        else:
214
            print("SOFTWARE RELEASE {0} NOT FOUND".format(softwareversion))
215
            cont = utilities.s2b(input("CONTINUE? Y/N: "))
216
            if not cont:
217
                print("\nEXITING...")
218
                raise SystemExit
219
    else:
220
        print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
221
222
223
def check_radio_sw(alturl, altsw, altchecked):
224
    """
225
    Check existence of radio software release.
226
227
    :param alturl: Radio base URL (from http to hashed SW release).
228
    :type alturl: str
229
230
    :param altsw: Radio software release.
231
    :type altsw: str
232
233
    :param altchecked: If we checked the sw release already.
234
    :type altchecked: bool
235
    """
236
    print("CHECKING RADIO SOFTWARE RELEASE...")
237
    if not altchecked:
238
        altavlty = networkutils.availability(alturl)
239
        if altavlty:
240
            print("SOFTWARE RELEASE {0} EXISTS".format(altsw))
241
        else:
242
            print("SOFTWARE RELEASE {0} NOT FOUND".format(altsw))
243
            cont = utilities.s2b(input("CONTINUE? Y/N: "))
244
            if not cont:
245
                print("\nEXITING...")
246
                raise SystemExit
247
    else:
248
        print("SOFTWARE RELEASE {0} EXISTS".format(altsw))
249
250
251
def check_os_single(osurl, osversion, device):
252
    """
253
    Check existence of single OS link.
254
255
    :param radiourl: Radio URL to check.
256
    :type radiourl: str
257
258
    :param radioversion: Radio version.
259
    :type radioversion: str
260
261
    :param device: Device family.
262
    :type device: int
263
    """
264
    osav = networkutils.availability(osurl)
265
    if not osav:
266
        print("{0} NOT AVAILABLE FOR {1}".format(osversion, bbconstants.DEVICES[device]))
267
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
268
        if not cont:
269
            print("\nEXITING...")
270
            raise SystemExit
271
272
273
def check_os_bulk(osurls):
274
    """
275
    Check existence of list of OS links.
276
277
    :param osurls: OS URLs to check.
278
    :type osurls: list(str)
279
    """
280
    for url in osurls:
281
        osav = networkutils.availability(url)
282
        if osav:
283
            break
284
    else:
285
        print("OS VERSION NOT FOUND")
286
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
287
        if not cont:
288
            print("\nEXITING...")
289
            raise SystemExit
290
291
292
def check_radio_single(radiourl, radioversion):
293
    """
294
    Check existence of single radio link.
295
296
    :param radiourl: Radio URL to check.
297
    :type radiourl: str
298
299
    :param radioversion: Radio version.
300
    :type radioversion: str
301
    """
302
    radav = networkutils.availability(radiourl)
303
    if not radav:
304
        print("RADIO VERSION NOT FOUND")
305
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
306
        if cont:
307
            rad2 = input("RADIO VERSION: ")
308
            radiourl = radiourl.replace(radioversion, rad2)
309
            radioversion = rad2
310
        else:
311
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
312
            if not going:
313
                print("\nEXITING...")
314
                raise SystemExit
315
    return radiourl, radioversion
316
317
318
def check_radio_bulk(radiourls, radioversion):
319
    """
320
    Check existence of list of radio links.
321
322
    :param radiourls: Radio URLs to check.
323
    :type radiourls: list(str)
324
325
    :param radioversion: Radio version.
326
    :type radioversion: str
327
    """
328
    for url in radiourls:
329
        radav = networkutils.availability(url)
330
        if radav:
331
            break
332
    else:
333
        print("RADIO VERSION NOT FOUND")
334
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
335
        if cont:
336
            rad2 = input("RADIO VERSION: ")
337
            radiourls = [url.replace(radioversion, rad2) for url in radiourls]
338
            radioversion = rad2
339
        else:
340
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
341
            if not going:
342
                print("\nEXITING...")
343
                raise SystemExit
344
    return radiourls, radioversion
345
346
347
def bulk_avail(urllist):
348
    """
349
    Filter 404 links out of URL list.
350
351
    :param urllist: URLs to check.
352
    :type urllist: list(str)
353
    """
354
    url2 = [x for x in urllist if networkutils.availability(x)]
355
    return url2
356
357
358
def get_baseurls(softwareversion, altsw=None):
359
    """
360
    Generate base URLs for bar links.
361
362
    :param softwareversion: Software version.
363
    :type softwareversion: str
364
365
    :param altsw: Radio software version, if necessary.
366
    :type altsw: str
367
    """
368
    baseurl = networkutils.create_base_url(softwareversion)
369
    alturl = networkutils.create_base_url(altsw) if altsw else None
370
    return baseurl, alturl
371
372
373
def get_sz_executable(compmethod):
374
    """
375
    Get 7z executable.
376
377
    :param compmethod: Compression method.
378
    :type compmethod: str
379
    """
380
    if compmethod != "7z":
381
        szexe = ""
382
    else:
383
        print("CHECKING PRESENCE OF 7ZIP...")
384
        psz = utilities.prep_seven_zip(True)
385
        if psz:
386
            print("7ZIP OK")
387
            szexe = utilities.get_seven_zip(False)
388
        else:
389
            szexe = ""
390
            print("7ZIP NOT FOUND")
391
            cont = utilities.s2b(input("CONTINUE? Y/N "))
392
            if cont:
393
                print("FALLING BACK TO ZIP...")
394
                compmethod = "zip"
395
            else:
396
                print("\nEXITING...")
397
                raise SystemExit  # bye bye
398
    return compmethod, szexe
399
400
401
def test_bar_files(localdir, urllist):
402
    """
403
    Test bar files after download.
404
405
    :param localdir: Directory.
406
    :type localdir: str
407
408
    :param urllist: List of URLs to check.
409
    :type urllist: list(str)
410
    """
411
    brokenlist = []
412
    print("TESTING BAR FILES...")
413
    for file in os.listdir(localdir):
414
        if file.endswith(".bar"):
415
            print("TESTING: {0}".format(file))
416
            thepath = os.path.abspath(os.path.join(localdir, file))
417
            brokens = barutils.bar_tester(thepath)
418
            if brokens is not None:
419
                os.remove(brokens)
420
                for url in urllist:
421
                    if brokens in url:
422
                        brokenlist.append(url)
423
    if brokenlist:
424
        print("SOME FILES ARE BROKEN!")
425
        utilities.lprint(brokenlist)
426
        raise SystemExit
427
    else:
428
        print("BAR FILES DOWNLOADED OK")
429
430
431
def test_signed_files(localdir):
432
    """
433
    Test signed files after extract.
434
435
    :param localdir: Directory.
436
    :type localdir: str
437
    """
438
    print("TESTING SIGNED FILES...")
439
    for file in os.listdir(localdir):
440
        if file.endswith(".bar"):
441
            print("TESTING: {0}".format(file))
442
            signname, signhash = barutils.retrieve_sha512(os.path.join(localdir, file))
443
            sha512ver = barutils.verify_sha512(os.path.join(localdir, signname.decode("utf-8")), signhash)
444
            if not sha512ver:
445
                print("{0} IS BROKEN".format((file)))
446
                break
447
    else:
448
        print("ALL FILES EXTRACTED OK")
449
450
451
def test_loader_files(localdir):
452
    """
453
    Test loader files after creation.
454
455
    :param localdir: Directory.
456
    :type localdir: str
457
    """
458
    if not utilities.is_windows():
459
        pass
460
    else:
461
        print("TESTING LOADER FILES...")
462
        brokens = utilities.verify_bulk_loaders(localdir)
463
        if brokens:
464
            print("BROKEN FILES:")
465
            utilities.lprint(brokens)
466
            raise SystemExit
467
        else:
468
            print("ALL FILES CREATED OK")
469
470
471
def test_single_loader(loaderfile):
472
    """
473
    Test single loader file after creation.
474
475
    :param loaderfile: File to check.
476
    :type loaderfile: str
477
    """
478
    if not utilities.is_windows():
479
        pass
480
    else:
481
        print("TESTING LOADER...")
482
        if not utilities.verify_loader_integrity(loaderfile):
483
            print("{0} IS BROKEN!".format(os.path.basename(loaderfile)))
484
            raise SystemExit
485
        else:
486
            print("LOADER CREATED OK")
487
488
489
def prod_avail(results, mailer=False, osversion=None, password=None):
490
    """
491
    Clean availability for production lookups for autolookup script.
492
493
    :param results: Result dict.
494
    :type results: dict(str: str)
495
496
    :param mailer: If we're mailing links. Default is false.
497
    :type mailer: bool
498
499
    :param osversion: OS version.
500
    :type osversion: str
501
502
    :param password: Email password.
503
    :type password: str
504
    """
505
    prel = results['p']
506
    if prel != "SR not in system" and prel is not None:
507
        pav = "PD"
508
        baseurl = networkutils.create_base_url(prel)
509
        avail = networkutils.availability(baseurl)
510
        is_avail = "Available" if avail else "Unavailable"
511
        if avail and mailer:
512
            sqlutils.prepare_sw_db()
513
            if not sqlutils.check_exists(osversion, prel):
514
                rad = utilities.increment(osversion, 1)
515
                linkgen(osversion, rad, prel, temp=True)
516
                smtputils.prep_email(osversion, prel, password)
517
    else:
518
        pav = "  "
519
        is_avail = "Unavailable"
520
    return prel, pav, is_avail
521
522
523
def linkgen(osversion, radioversion=None, softwareversion=None, altsw=None, temp=False, sdk=False):
524
    """
525
    Generate debrick/core/radio links for given OS, radio, software release.
526
527
    :param osversion: OS version, 10.x.y.zzzz.
528
    :type osversion: str
529
530
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
531
    :type radioversion: str
532
533
    :param softwareversion: Software version, 10.x.y.zzzz. Can be guessed.
534
    :type softwareversion: str
535
536
    :param altsw: Radio software release, if not the same as OS.
537
    :type altsw: str
538
539
    :param temp: If file we write to is temporary. Default is False.
540
    :type temp: bool
541
542
    :param sdk: If we specifically want SDK images. Default is False.
543
    :type sdk: bool
544
    """
545
    radioversion = return_radio_version(osversion, radioversion)
546
    softwareversion, swc = return_sw_checked(softwareversion, osversion)
547
    del swc
548
    if altsw is not None:
549
        altsw, aswc = return_radio_sw_checked(altsw, radioversion)
550
        del aswc
551
    baseurl = networkutils.create_base_url(softwareversion)
552
    oses, cores, radios = textgenerator.url_gen(osversion, radioversion, softwareversion)
553
    if altsw is not None:
554
        del radios
555
        dbks, cors, radios = textgenerator.url_gen(osversion, radioversion, altsw)
556
        del dbks
557
        del cors
558
    avlty = networkutils.availability(baseurl)
559
    if sdk:
560
        oses2 = {key: val.replace("factory_sfi", "sdk") for key, val in oses.items()}
561
        cores2 = {key: val.replace("factory_sfi", "sdk") for key, val in cores.items()}
562
        oses = {key: val.replace("verizon_sfi", "sdk") for key, val in oses2.items()}
563
        cores = {key: val.replace("verizon_sfi", "sdk") for key, val in cores2.items()}
564
    prargs = (softwareversion, osversion, radioversion, oses, cores, radios, avlty, False, None, temp, altsw)
565
    _thread.start_new_thread(textgenerator.write_links, prargs)
566
567
568
def clean_swrel(swrelset):
569
    """
570
    Clean a list of software release lookups.
571
572
    :param swrelset: List of software releases.
573
    :type swrelset: set(str)
574
    """
575
    for i in swrelset:
576
        if i != "SR not in system" and i is not None:
577
            swrelease = i
578
            break
579
    else:
580
        swrelease = ""
581
    return swrelease
582
583
584
def autolookup_logger(record, out):
585
    """
586
    Write autolookup results to file.
587
588
    :param record: The file to log to.
589
    :type record: str
590
591
    :param out: Output block.
592
    :type out: str
593
    """
594
    with open(record, "a") as rec:
595
        rec.write("{0}\n".format(out))
596
597
598
def autolookup_printer(out, avail, log=False, quiet=False, record=None):
599
    """
600
    Print autolookup results, logging if specified.
601
602
    :param out: Output block.
603
    :type out: str
604
605
    :param avail: Availability. Can be "Available" or "Unavailable".
606
    :type avail: str
607
608
    :param log: If we're logging to file.
609
    :type log: bool
610
611
    :param quiet: If we only note available entries.
612
    :type quiet: bool
613
614
    :param record: If we're logging, the file to log to.
615
    :type record: str
616
    """
617
    if not quiet:
618
        avail = "Available"  # force things
619
    if avail.lower() == "available":
620
        if log:
621
            _thread.start_new_thread(autolookup_logger, (record, out))
622
        print(out)
623
624
625
def autolookup_output(osversion, swrelease, avail, avpack, sql=False):
626
    """
627
    Prepare autolookup block, and add to SQL database.
628
629
    :param osversion: OS version.
630
    :type osversion: str
631
632
    :param swrelease: Software release.
633
    :type swrelease: str
634
635
    :param avail: "Unavailable" or "Available".
636
    :type avail: str
637
638
    :param avpack: Availabilities: alpha 1 and 2, beta 1 and 2, production.
639
    :type avpack: list(str)
640
641
    :param sql: If we're adding this to our SQL database.
642
    :type sql: bool
643
    """
644
    if sql:
645
        sqlutils.prepare_sw_db()
646
        if not sqlutils.check_exists(osversion, swrelease):
647
            sqlutils.insert(osversion, swrelease, avail.lower())
648
    avblok = "[{0}|{1}|{2}|{3}|{4}]".format(*avpack)
649
    out = "OS {0} - SR {1} - {2} - {3}".format(osversion, swrelease, avblok, avail)
650
    return out
651
652
653
def export_cchecker(files, npc, hwid, osv, radv, swv, upgrade=False, forced=None):
654
    """
655
    Write carrierchecker lookup links to file.
656
657
    :param files: List of file URLs.
658
    :type files: list(str)
659
660
    :param npc: MCC + MNC (ex. 302220).
661
    :type npc: int
662
663
    :param hwid: Device hardware ID.
664
    :type hwid: str
665
666
    :param osv: OS version.
667
    :type osv: str
668
669
    :param radv: Radio version.
670
    :type radv: str
671
672
    :param swv: Software release.
673
    :type swv: str
674
675
    :param upgrade: Whether or not to use upgrade files. Default is false.
676
    :type upgrade: bool
677
678
    :param forced: Force a software release. None to go for latest.
679
    :type forced: str
680
    """
681
    if files:
682
        if not upgrade:
683
            newfiles = networkutils.carrier_query(npc, hwid, True, False, forced)
684
            cleanfiles = newfiles[3]
685
        else:
686
            cleanfiles = files
687
        osurls, coreurls, radiourls = textgenerator.url_gen(osv, radv, swv)
688
        stoppers = ["8960", "8930", "8974", "m5730", "winchester"]
689
        finals = [link for link in cleanfiles if all(word not in link for word in stoppers)]
690
        textgenerator.write_links(swv, osv, radv, osurls, coreurls, radiourls, True, True, finals)
691
        print("\nFINISHED!!!")
692
    else:
693
        print("CANNOT EXPORT, NO SOFTWARE RELEASE")
694
695
696
def generate_blitz_links(files, osv, radv, swv):
697
    """
698
    Generate blitz URLs (i.e. all OS and radio links).
699
    :param files: List of file URLs.
700
    :type files: list(str)
701
702
    :param osv: OS version.
703
    :type osv: str
704
705
    :param radv: Radio version.
706
    :type radv: str
707
708
    :param swv: Software release.
709
    :type swv: str
710
    """
711
    baseurl = networkutils.create_base_url(swv)
712
    suffix = "nto+armle-v7+signed.bar"
713
    coreurls = [
714
        "{0}/winchester.factory_sfi-{1}-{2}".format(baseurl, osv, suffix),
715
        "{0}/qc8960.factory_sfi-{1}-{2}".format(baseurl, osv, suffix),
716
        "{0}/qc8960.factory_sfi_hybrid_qc8x30-{1}-{2}".format(baseurl, osv, suffix),
717
        "{0}/qc8974.factory_sfi_hybrid_qc8974-{1}-{2}".format(baseurl, osv, suffix)
718
    ]
719
    radiourls = [
720
        "{0}/m5730-{1}-{2}".format(baseurl, radv, suffix),
721
        "{0}/qc8960-{1}-{2}".format(baseurl, radv, suffix),
722
        "{0}/qc8960.wtr-{1}-{2}".format(baseurl, radv, suffix),
723
        "{0}/qc8960.wtr5-{1}-{2}".format(baseurl, radv, suffix),
724
        "{0}/qc8930.wtr5-{1}-{2}".format(baseurl, radv, suffix),
725
        "{0}/qc8974.wtr2-{1}-{2}".format(baseurl, radv, suffix)
726
    ]
727
    return files + coreurls + radiourls
728
729
730
def package_blitz(bardir, swv):
731
    """
732
    Package and verify a blitz package.
733
734
    :param bardir: Path to folder containing bar files.
735
    :type bardir: str
736
737
    :param swv: Software version.
738
    :type swv: str
739
    """
740
    print("\nCREATING BLITZ...")
741
    barutils.create_blitz(bardir, swv)
742
    print("\nTESTING BLITZ...")
743
    zipver = archiveutils.zip_verify("Blitz-{0}.zip".format(swv))
744
    if not zipver:
745
        print("BLITZ FILE IS BROKEN")
746
        raise SystemExit
747
    else:
748
        shutil.rmtree(bardir)
749
750
751
def purge_dross(files, craplist):
752
    """
753
    Get rid of Nuance/retaildemo apps in a list of apps.
754
755
    :param files: List of URLs to clean.
756
    :type files: list(str)
757
758
    :param craplist: List of fragments to check for and remove.
759
    :type craplist: list(str)
760
    """
761
    files2 = [file for file in files if all(word not in file for word in craplist)]
762
    return files2
763
764
765
def slim_preamble(appname):
766
    """
767
    Standard app name header.
768
769
    :param appname: Name of app.
770
    :type appname: str
771
    """
772
    print("~~~{0} VERSION {1}~~~".format(appname.upper(), shortversion()))
773
774
775
def standard_preamble(appname, osversion, softwareversion, radioversion, altsw=None):
776
    """
777
    Standard app name, OS, radio and software (plus optional radio software) print block.
778
779
    :param appname: Name of app.
780
    :type appname: str
781
782
    :param osversion: OS version, 10.x.y.zzzz. Required.
783
    :type osversion: str
784
785
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
786
    :type radioversion: str
787
788
    :param softwareversion: Software release, 10.x.y.zzzz. Can be guessed.
789
    :type softwareversion: str
790
791
    :param altsw: Radio software release, if not the same as OS.
792
    :type altsw: str
793
    """
794
    slim_preamble(appname)
795
    print("OS VERSION: {0}".format(osversion))
796
    print("OS SOFTWARE VERSION: {0}".format(softwareversion))
797
    print("RADIO VERSION: {0}".format(radioversion))
798
    if altsw is not None:
799
        print("RADIO SOFTWARE VERSION: {0}".format(altsw))
800
801
802
def verify_gpg_credentials():
803
    """
804
    Read GPG key/pass from file, verify if incomplete.
805
    """
806
    gpgkey, gpgpass = hashutils.gpg_config_loader()
807
    if gpgkey is None or gpgpass is None:
808
        print("NO PGP KEY/PASS FOUND")
809
        cont = utilities.s2b(input("CONTINUE (Y/N)?: "))
810
        if cont:
811
            if gpgkey is None:
812
                gpgkey = input("PGP KEY (0x12345678): ")
813
                if not gpgkey.startswith("0x"):
814
                    gpgkey = "0x{0}".format(gpgkey)   # add preceding 0x
815
            if gpgpass is None:
816
                gpgpass = getpass.getpass(prompt="PGP PASSPHRASE: ")
817
                writebool = utilities.s2b(input("SAVE PASSPHRASE (Y/N)?:"))
818
            else:
819
                writebool = False
820
            gpgpass2 = gpgpass if writebool else None
821
            hashutils.gpg_config_writer(gpgkey, gpgpass2)
822
        else:
823
            gpgkey = None
824
    return gpgkey, gpgpass
825
826
827
def bulk_hash(dirs, compressed=True, deleted=True, radios=True, hashdict=None):
828
    """
829
    Hash files in several folders based on flags.
830
831
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
832
    :type dirs: list(str)
833
834
    :param compressed: Whether to hash compressed files. True by default.
835
    :type compressed: bool
836
837
    :param deleted: Whether to delete uncompressed files. True by default.
838
    :type deleted: bool
839
840
    :param radios: Whether to hash radio autoloaders. True by default.
841
    :type radios: bool
842
843
    :param hashdict: Dictionary of hash rules, in ~\bbarchivist.ini.
844
    :type hashdict: dict({str: bool})
845
    """
846
    print("HASHING LOADERS...")
847
    if compressed:
848
        hashutils.verifier(dirs[4], hashdict)
849
        if radios:
850
            hashutils.verifier(dirs[5], hashdict)
851
    if not deleted:
852
        hashutils.verifier(dirs[2], hashdict)
853
        if radios:
854
            hashutils.verifier(dirs[3], hashdict)
855
856
857
def bulk_verify(dirs, compressed=True, deleted=True, radios=True):
858
    """
859
    Verify files in several folders based on flags.
860
861
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
862
    :type dirs: list(str)
863
864
    :param compressed: Whether to hash compressed files. True by default.
865
    :type compressed: bool
866
867
    :param deleted: Whether to delete uncompressed files. True by default.
868
    :type deleted: bool
869
870
    :param radios: Whether to hash radio autoloaders. True by default.
871
    :type radios: bool
872
    """
873
    gpgkey, gpgpass = verify_gpg_credentials()
874
    if gpgpass is not None and gpgkey is not None:
875
        print("VERIFYING LOADERS...")
876
        print("KEY: {0}".format(gpgkey))
877
        if compressed:
878
            hashutils.gpgrunner(dirs[4], gpgkey, gpgpass, True)
879
            if radios:
880
                hashutils.gpgrunner(dirs[5], gpgkey, gpgpass, True)
881
        if not deleted:
882
            hashutils.gpgrunner(dirs[2], gpgkey, gpgpass, True)
883
            if radios:
884
                hashutils.gpgrunner(dirs[3], gpgkey, gpgpass, True)
885
886
887
def enn_ayy(quant):
888
    """
889
    Cheeky way to put a N/A placeholder for a string.
890
891
    :param quant: What to check if it's None.
892
    :type quant: str
893
    """
894
    return "N/A" if quant is None else quant
895
896
897
def info_header(afile, osver, radio=None, software=None, device=None):
898
    """
899
    Write header for info file.
900
901
    :param afile: Open file to write to.
902
    :type afile: File object
903
904
    :param osver: OS version, required for both types.
905
    :type osver: str
906
907
    :param radio: Radio version, required for QNX.
908
    :type radio: str
909
910
    :param software: Software release, required for QNX.
911
    :type software: str
912
913
    :param device: Device type, required for Android.
914
    :type device: str
915
    """
916
    afile.write("OS: {0}\n".format(osver))
917
    if device:
918
        afile.write("Device: {0}\n".format(enn_ayy(device)))
919
    else:
920
        afile.write("Radio: {0}\n".format(enn_ayy(radio)))
921
        afile.write("Software: {0}\n".format(enn_ayy(software)))
922
    afile.write("{0}\n".format("~"*40))
923
924
925
def make_info(filepath, osver, radio=None, software=None, device=None):
926
    """
927
    Create a new-style info (names, sizes and hashes) file.
928
929
    :param filepath: Path to folder to analyze.
930
    :type filepath: str
931
932
    :param osver: OS version, required for both types.
933
    :type osver: str
934
935
    :param radio: Radio version, required for QNX.
936
    :type radio: str
937
938
    :param software: Software release, required for QNX.
939
    :type software: str
940
941
    :param device: Device type, required for Android.
942
    :type device: str
943
    """
944
    fileext = ".zip" if device else ".7z"
945
    files = os.listdir(filepath)
946
    absfiles = [os.path.join(filepath, x) for x in files if x.endswith((fileext, ".exe"))]
947
    fname = os.path.join(filepath, "!{0}_OSINFO!.txt".format(osver))
948
    with open(fname, "w") as afile:
949
        info_header(afile, osver, radio, software, device)
950
        for indx, file in enumerate(absfiles):
951
            fsize = os.stat(file).st_size
952
            afile.write("File: {0}\n".format(os.path.basename(file)))
953
            afile.write("\tSize: {0} ({1})\n".format(fsize, utilities.fsizer(fsize)))
954
            afile.write("\tHashes:\n")
955
            afile.write("\t\tMD5: {0}\n".format(hashutils.hm5(file).upper()))
956
            afile.write("\t\tSHA1: {0}\n".format(hashutils.hs1(file).upper()))
957
            afile.write("\t\tSHA256: {0}\n".format(hashutils.hs256(file).upper()))
958
            afile.write("\t\tSHA512: {0}\n".format(hashutils.hs512(file).upper()))
959
            if indx != len(absfiles) - 1:
960
                afile.write("\n")
961
962
963
def bulk_info(dirs, osv, compressed=True, deleted=True, radios=True, rad=None, swv=None, dev=None):
964
    """
965
    Generate info files in several folders based on flags.
966
967
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
968
    :type dirs: list(str)
969
970
    :param osver: OS version, required for both types.
971
    :type osver: str
972
973
    :param compressed: Whether to hash compressed files. True by default.
974
    :type compressed: bool
975
976
    :param deleted: Whether to delete uncompressed files. True by default.
977
    :type deleted: bool
978
979
    :param radios: Whether to hash radio autoloaders. True by default.
980
    :type radios: bool
981
982
    :param rad: Radio version, required for QNX.
983
    :type rad: str
984
985
    :param swv: Software release, required for QNX.
986
    :type swv: str
987
988
    :param dev: Device type, required for Android.
989
    :type dev: str
990
    """
991
    print("GENERATING INFO FILES...")
992
    if compressed:
993
        make_info(dirs[4], osv, rad, swv, dev)
994
        if radios:
995
            make_info(dirs[5], osv, rad, swv, dev)
996
    if not deleted:
997
        make_info(dirs[2], osv, rad, swv, dev)
998
        if radios:
999
            make_info(dirs[3], osv, rad, swv, dev)
1000