Completed
Push — master ( 3e3e86...d14a39 )
by John
01:59
created

generic_windows_shim()   B

Complexity

Conditions 2

Size

Total Lines 24

Duplication

Lines 17
Ratio 70.83 %

Importance

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