Completed
Push — master ( c6611a...9b5ff2 )
by John
01:12
created

autolookup_output()   A

Complexity

Conditions 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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