Completed
Push — master ( acfaee...c48b51 )
by John
06:00
created

tcl_mainscan_printer()   A

Complexity

Conditions 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 17
ccs 3
cts 3
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
#!/usr/bin/env python3
2 5
"""This module contains various utilities for the scripts folder."""
3
4 5
import os  # path work
5 5
import getpass  # invisible password
6 5
import argparse  # generic parser
7 5
import hashlib  # hashes
8 5
import sys  # getattr
9 5
import shutil  # folder removal
10 5
import subprocess  # running cfp/cap
11 5
import glob  # file lookup
12 5
import threading  # run stuff in background
13 5
import requests  # session
14 5
from bbarchivist import utilities  # little things
15 5
from bbarchivist import decorators  # decorating functions
16 5
from bbarchivist import barutils  # file system work
17 5
from bbarchivist import archiveutils  # archive support
18 5
from bbarchivist import bbconstants  # constants
19 5
from bbarchivist import gpgutils  # gpg
20 5
from bbarchivist import hashutils  # file hashes
21 5
from bbarchivist import networkutils  # network tools
22 5
from bbarchivist import textgenerator  # writing text to file
23 5
from bbarchivist import smtputils  # email
24 5
from bbarchivist import sqlutils  # sql
25
26 5
__author__ = "Thurask"
27 5
__license__ = "WTFPL v2"
28 5
__copyright__ = "2015-2017 Thurask"
29
30
31 5
def shortversion():
32
    """
33
    Get short app version (Git tag).
34
    """
35 5
    if not getattr(sys, 'frozen', False):
36 5
        ver = bbconstants.VERSION
37
    else:
38 5
        verfile = glob.glob(os.path.join(os.getcwd(), "version.txt"))[0]
39 5
        with open(verfile) as afile:
40 5
            ver = afile.read()
41 5
    return ver
42
43
44 5
def longversion():
45
    """
46
    Get long app version (Git tag + commits + hash).
47
    """
48 5
    if not getattr(sys, 'frozen', False):
49 5
        ver = (bbconstants.LONGVERSION, bbconstants.COMMITDATE)
50
    else:
51 5
        verfile = glob.glob(os.path.join(os.getcwd(), "longversion.txt"))[0]
52 5
        with open(verfile) as afile:
53 5
            ver = afile.read().split("\n")
54 5
    return ver
55
56
57 5
def default_parser_vers(vers=None):
58
    """
59
    Prepare version for default parser.
60
61
    :param vers: Versions: [git commit hash, git commit date]
62
    :param vers: list(str)
63
    """
64 5
    if vers is None:
65 5
        vers = longversion()
66 5
    return vers
67
68
69 5
def default_parser_flags(parser, flags=None):
70
    """
71
    Handle flags for default parser.
72
73
    :param parser: Parser to modify.
74
    :type parser: argparse.ArgumentParser
75
76
    :param flags: Tuple of sections to add.
77
    :type flags: tuple(str)
78
    """
79 5
    if flags is not None:
80 5
        parser = dpf_flags_folder(parser, flags)
81 5
        parser = dpf_flags_osr(parser, flags)
82 5
    return parser
83
84
85 5
def dpf_flags_folder(parser, flags=None):
86
    """
87
    Add generic folder flag to parser.
88
89
    :param parser: Parser to modify.
90
    :type parser: argparse.ArgumentParser
91
92
    :param flags: Tuple of sections to add.
93
    :type flags: tuple(str)
94
    """
95 5
    if "folder" in flags:
96 5
        parser.add_argument("-f", "--folder", dest="folder", help="Working folder", default=None, metavar="DIR", type=utilities.file_exists)
97 5
    return parser
98
99
100 5
def dpf_flags_osr(parser, flags=None):
101
    """
102
    Add generic OS/radio/software flags to parser.
103
104
    :param parser: Parser to modify.
105
    :type parser: argparse.ArgumentParser
106
107
    :param flags: Tuple of sections to add.
108
    :type flags: tuple(str)
109
    """
110 5
    if "osr" in flags:
111 5
        parser.add_argument("os", help="OS version")
112 5
        parser.add_argument("radio", help="Radio version, 10.x.y.zzzz", nargs="?", default=None)
113 5
        parser.add_argument("swrelease", help="Software version, 10.x.y.zzzz", nargs="?", default=None)
114 5
    return parser
115
116
117 5
def default_parser(name=None, desc=None, flags=None, vers=None):
118
    """
119
    A generic form of argparse's ArgumentParser.
120
121
    :param name: App name.
122
    :type name: str
123
124
    :param desc: App description.
125
    :type desc: str
126
127
    :param flags: Tuple of sections to add.
128
    :type flags: tuple(str)
129
130
    :param vers: Versions: [git commit hash, git commit date]
131
    :param vers: list(str)
132
    """
133 5
    vers = default_parser_vers(vers)
134 5
    parser = argparse.ArgumentParser(prog=name, description=desc, epilog="https://github.com/thurask/bbarchivist")
135 5
    parser.add_argument("-v", "--version", action="version", version="{0} {1} committed {2}".format(parser.prog, vers[0], vers[1]))
136 5
    parser = default_parser_flags(parser, flags)
137 5
    return parser
138
139
140 5
def generic_windows_shim(scriptname, scriptdesc, target, version):
141
    """
142
    Generic CFP/CAP runner; Windows only.
143
144
    :param scriptname: Script name, 'bb-something'.
145
    :type scriptname: str
146
147
    :param scriptdesc: Script description, i.e. scriptname -h.
148
    :type scriptdesc: str
149
150
    :param target: Path to file to execute.
151
    :type target: str
152
153
    :param version: Version of target.
154
    :type version: str
155
    """
156 5
    parser = default_parser(scriptname, scriptdesc)
157 5
    capver = "|{0}".format(version)
158 5
    parser = external_version(parser, capver)
159 5
    parser.parse_known_args(sys.argv[1:])
160 5
    if utilities.is_windows():
161 5
        subprocess.call([target] + sys.argv[1:])
162
    else:
163 5
        print("Sorry, Windows only.")
164
165
166 5
def arg_verify_none(argval, message):
167
    """
168
    Check if an argument is None, error out if it is.
169
170
    :param argval: Argument to check.
171
    :type argval: str
172
173
    :param message: Error message to print.
174
    :type message: str
175
    """
176 5
    if argval is None:
177 5
        raise argparse.ArgumentError(argument=None, message=message)
178
179
180 5
def external_version(parser, addition):
181
    """
182
    Modify the version string of argparse.ArgumentParser, adding something.
183
184
    :param parser: Parser to modify.
185
    :type parser: argparse.ArgumentParser
186
187
    :param addition: What to add.
188
    :type addition: str
189
    """
190 5
    verarg = [arg for arg in parser._actions if isinstance(arg, argparse._VersionAction)][0]
191 5
    verarg.version = "{1}{0}".format(addition, verarg.version)
192 5
    return parser
193
194
195 5
def return_radio_version(osversion, radioversion=None):
196
    """
197
    Increment radio version, if need be.
198
199
    :param osversion: OS version.
200
    :type osversion: str
201
202
    :param radioversion: Radio version, None if incremented.
203
    :type radioversion: str
204
    """
205 5
    if radioversion is None:
206 5
        radioversion = utilities.increment(osversion, 1)
207 5
    return radioversion
208
209
210 5
def sw_check_contingency(softwareversion):
211
    """
212
    Ask in the event software release isn't found.
213
214
    :param softwareversion: Software release version.
215
    :type softwareversion: str
216
    """
217 5
    if softwareversion == "SR not in system":
218 5
        print("SOFTWARE RELEASE NOT FOUND")
219 5
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
220 5
        if cont:
221 5
            softwareversion = input("SOFTWARE RELEASE: ")
222 5
            swchecked = False
223
        else:
224 5
            print("\nEXITING...")
225 5
            raise SystemExit  # bye bye
226
    else:
227 5
        swchecked = True
228 5
    return softwareversion, swchecked
229
230
231 5
def return_sw_checked(softwareversion, osversion):
232
    """
233
    Check software existence, return boolean.
234
235
    :param softwareversion: Software release version.
236
    :type softwareversion: str
237
238
    :param osversion: OS version.
239
    :type osversion: str
240
    """
241 5
    if softwareversion is None:
242 5
        serv = bbconstants.SERVERS["p"]
243 5
        softwareversion = networkutils.sr_lookup(osversion, serv)
244 5
        softwareversion, swchecked = sw_check_contingency(softwareversion)
245
    else:
246 5
        swchecked = True
247 5
    return softwareversion, swchecked
248
249
250 5
def return_radio_sw_checked(altsw, radioversion):
251
    """
252
    Check radio software existence, return boolean.
253
254
    :param altsw: Software release version.
255
    :type altsw: str
256
257
    :param radioversion: Radio version.
258
    :type radioversion: str
259
    """
260 5
    if altsw == "checkme":
261 5
        serv = bbconstants.SERVERS["p"]
262 5
        testos = utilities.increment(radioversion, -1)
263 5
        altsw = networkutils.sr_lookup(testos, serv)
264 5
        altsw, altchecked = sw_check_contingency(altsw)
265
    else:
266 5
        altchecked = True
267 5
    return altsw, altchecked
268
269
270 5
def check_sw(baseurl, softwareversion, swchecked, altsw=False):
271
    """
272
    Check existence of software release.
273
274
    :param baseurl: Base URL (from http to hashed SW release).
275
    :type baseurl: str
276
277
    :param softwareversion: Software release.
278
    :type softwareversion: str
279
280
    :param swchecked: If we checked the sw release already.
281
    :type swchecked: bool
282
283
    :param altsw: If this is the radio-only release. Default is false.
284
    :type altsw: bool
285
    """
286 5
    message = "CHECKING RADIO SOFTWARE RELEASE..." if altsw else "CHECKING SOFTWARE RELEASE..."
287 5
    print(message)
288 5
    if not swchecked:
289 5
        check_sw_actual(baseurl, softwareversion)
290
    else:
291 5
        print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
292
293
294 5
def check_sw_actual(baseurl, softwareversion):
295
    """
296
    Get the status of a software release.
297
298
    :param baseurl: Base URL (from http to hashed SW release).
299
    :type baseurl: str
300
301
    :param softwareversion: Software release.
302
    :type softwareversion: str
303
    """
304 5
    avlty = networkutils.availability(baseurl)
305 5
    if avlty:
306 5
        print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
307
    else:
308 5
        check_sw_handle(softwareversion)
309
310
311 5
def check_sw_handle(softwareversion):
312
    """
313
    Handle non-existent software release.
314
315
    :param softwareversion: Software release.
316
    :type softwareversion: str
317
    """
318 5
    print("SOFTWARE RELEASE {0} NOT FOUND".format(softwareversion))
319 5
    cont = utilities.s2b(input("CONTINUE? Y/N: "))
320 5
    if not cont:
321 5
        print("\nEXITING...")
322 5
        raise SystemExit
323
324
325 5
def check_radio_sw(alturl, altsw, altchecked):
326
    """
327
    Check existence of radio software release.
328
329
    :param alturl: Radio base URL (from http to hashed SW release).
330
    :type alturl: str
331
332
    :param altsw: Radio software release.
333
    :type altsw: str
334
335
    :param altchecked: If we checked the sw release already.
336
    :type altchecked: bool
337
    """
338 5
    return check_sw(alturl, altsw, altchecked, True)
339
340
341 5
def check_altsw(altcheck=False):
342
    """
343
    Ask for and return alternate software release, if needed.
344
345
    :param altcheck: If we're using an alternate software release.
346
    :type altcheck: bool
347
    """
348 5
    if altcheck:
349 5
        altsw = input("RADIO SOFTWARE RELEASE (PRESS ENTER TO GUESS): ")
350 5
        if not altsw:
351 5
            altsw = "checkme"
352
    else:
353 5
        altsw = None
354 5
    return altsw
355
356
357 5
def check_os_single(osurl, osversion, device):
358
    """
359
    Check existence of single OS link.
360
361
    :param radiourl: Radio URL to check.
362
    :type radiourl: str
363
364
    :param radioversion: Radio version.
365
    :type radioversion: str
366
367
    :param device: Device family.
368
    :type device: int
369
    """
370 5
    osav = networkutils.availability(osurl)
371 5
    if not osav:
372 5
        print("{0} NOT AVAILABLE FOR {1}".format(osversion, bbconstants.DEVICES[device]))
373 5
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
374 5
        if not cont:
375 5
            print("\nEXITING...")
376 5
            raise SystemExit
377
378
379 5
def check_os_bulk(osurls):
380
    """
381
    Check existence of list of OS links.
382
383
    :param osurls: OS URLs to check.
384
    :type osurls: list(str)
385
    """
386 5
    sess = requests.Session()
387 5
    for url in osurls:
388 5
        osav = networkutils.availability(url, sess)
389 5
        if osav:
390 5
            break
391
    else:
392 5
        check_os_bulk_handle()
393
394
395 5
def check_os_bulk_handle():
396
    """
397
    Handle no existing OS links.
398
    """
399 5
    print("OS VERSION NOT FOUND")
400 5
    cont = utilities.s2b(input("CONTINUE? Y/N: "))
401 5
    if not cont:
402 5
        print("\nEXITING...")
403 5
        raise SystemExit
404
405
406 5
def check_radio_single(radiourl, radioversion):
407
    """
408
    Check existence of single radio link.
409
410
    :param radiourl: Radio URL to check.
411
    :type radiourl: str
412
413
    :param radioversion: Radio version.
414
    :type radioversion: str
415
    """
416 5
    radav = networkutils.availability(radiourl)
417 5
    if not radav:
418 5
        print("RADIO VERSION NOT FOUND")
419 5
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
420 5
        if cont:
421 5
            rad2 = input("RADIO VERSION: ")
422 5
            radiourl = radiourl.replace(radioversion, rad2)
423 5
            radioversion = rad2
424
        else:
425 5
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
426 5
            if not going:
427 5
                print("\nEXITING...")
428 5
                raise SystemExit
429 5
    return radiourl, radioversion
430
431
432 5
def check_radio_bulk(radiourls, radioversion):
433
    """
434
    Check existence of list of radio links.
435
436
    :param radiourls: Radio URLs to check.
437
    :type radiourls: list(str)
438
439
    :param radioversion: Radio version.
440
    :type radioversion: str
441
    """
442 5
    sess = requests.Session()
443 5
    for url in radiourls:
444 5
        radav = networkutils.availability(url, sess)
445 5
        if radav:
446 5
            break
447
    else:
448 5
        radiourls, radioversion = check_radio_bulk_notfound(radiourls, radioversion)
449 5
    return radiourls, radioversion
450
451
452 5
def check_radio_bulk_notfound(radiourls, radioversion):
453
    """
454
    What to do if radio links aren't found.
455
456
    :param radiourls: Radio URLs to check.
457
    :type radiourls: list(str)
458
459
    :param radioversion: Radio version.
460
    :type radioversion: str
461
    """
462 5
    print("RADIO VERSION NOT FOUND")
463 5
    cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
464 5
    if cont:
465 5
        radiourls, radioversion = check_radio_bulk_go(radiourls, radioversion)
466
    else:
467 5
        check_radio_bulk_stop()
468 5
    return radiourls, radioversion
469
470
471 5
def check_radio_bulk_go(radiourls, radioversion):
472
    """
473
    Replace radio version and URLs, and keep going.
474
475
    :param radiourls: Radio URLs to check.
476
    :type radiourls: list(str)
477
478
    :param radioversion: Radio version.
479
    :type radioversion: str
480
    """
481 5
    rad2 = input("RADIO VERSION: ")
482 5
    radiourls = [url.replace(radioversion, rad2) for url in radiourls]
483 5
    radioversion = rad2
484 5
    return radiourls, radioversion
485
486
487 5
def check_radio_bulk_stop():
488
    """
489
    Ask if we should keep going once no radio has been found.
490
    """
491 5
    going = utilities.s2b(input("KEEP GOING? Y/N: "))
492 5
    if not going:
493 5
        print("\nEXITING...")
494 5
        raise SystemExit
495
496
497 5
def bulk_avail(urllist):
498
    """
499
    Filter 404 links out of URL list.
500
501
    :param urllist: URLs to check.
502
    :type urllist: list(str)
503
    """
504 5
    sess = requests.Session()
505 5
    url2 = [x for x in urllist if networkutils.availability(x, sess)]
506 5
    return url2
507
508
509 5
def get_baseurls(softwareversion, altsw=None):
510
    """
511
    Generate base URLs for bar links.
512
513
    :param softwareversion: Software version.
514
    :type softwareversion: str
515
516
    :param altsw: Radio software version, if necessary.
517
    :type altsw: str
518
    """
519 5
    baseurl = utilities.create_base_url(softwareversion)
520 5
    alturl = utilities.create_base_url(altsw) if altsw else None
521 5
    return baseurl, alturl
522
523
524 5
def get_sz_executable(compmethod):
525
    """
526
    Get 7z executable.
527
528
    :param compmethod: Compression method.
529
    :type compmethod: str
530
    """
531 5
    if compmethod != "7z":
532 5
        szexe = ""
533
    else:
534 5
        print("CHECKING PRESENCE OF 7ZIP...")
535 5
        psz = utilities.prep_seven_zip(True)
536 5
        if psz:
537 5
            print("7ZIP OK")
538 5
            szexe = utilities.get_seven_zip(False)
539
        else:
540 5
            szexe = ""
541 5
            print("7ZIP NOT FOUND")
542 5
            cont = utilities.s2b(input("CONTINUE? Y/N "))
543 5
            if cont:
544 5
                print("FALLING BACK TO ZIP...")
545 5
                compmethod = "zip"
546
            else:
547 5
                print("\nEXITING...")
548 5
                raise SystemExit  # bye bye
549 5
    return compmethod, szexe
550
551
552 5
def test_bar_files(localdir, urllist):
553
    """
554
    Test bar files after download.
555
556
    :param localdir: Directory.
557
    :type localdir: str
558
559
    :param urllist: List of URLs to check.
560
    :type urllist: list(str)
561
    """
562 5
    print("TESTING BAR FILES...")
563 5
    brokenlist = []
564 5
    for file in os.listdir(localdir):
565 5
        brokenlist = test_bar_files_individual(file, localdir, urllist, brokenlist)
566 5
    if brokenlist:
567 5
        print("SOME FILES ARE BROKEN!")
568 5
        utilities.lprint(brokenlist)
569 5
        raise SystemExit
570
    else:
571 5
        print("BAR FILES DOWNLOADED OK")
572
573
574 5
def test_bar_files_individual(file, localdir, urllist, brokenlist):
575
    """
576
    Test bar file after download.
577
578
    :param file: Bar file to check.
579
    :type file: str
580
581
    :param localdir: Directory.
582
    :type localdir: str
583
584
    :param urllist: List of URLs to check.
585
    :type urllist: list(str)
586
587
    :param brokenlist: List of URLs to download later.
588
    :type brokenlist: list(str)
589
    """
590 5
    if file.endswith(".bar"):
591 5
        print("TESTING: {0}".format(file))
592 5
        thepath = os.path.abspath(os.path.join(localdir, file))
593 5
        brokens = barutils.bar_tester(thepath)
594 5
        brokenlist = bar_broken_individual(brokens, urllist, brokenlist)
595 5
    return brokenlist
596
597
598 5
def bar_broken_individual(brokens, urllist, brokenlist):
599
    """
600
    What to do if a downloaded bar file is broken.
601
602
    :param brokens: None if bar is OK, filename if it is not.
603
    :type brokens: str
604
605
    :param urllist: List of URLs to check.
606
    :type urllist: list(str)
607
608
    :param brokenlist: List of URLs to download later.
609
    :type brokenlist: list(str)
610
    """
611 5
    if brokens is not None:
612 5
        os.remove(brokens)
613 5
        for url in urllist:
614 5
            if brokens in url:
615 5
                brokenlist.append(url)
616 5
    return brokenlist
617
618
619 5
def test_signed_files(localdir):
620
    """
621
    Test signed files after extract.
622
623
    :param localdir: Directory.
624
    :type localdir: str
625
    """
626 5
    print("TESTING SIGNED FILES...")
627 5
    for file in os.listdir(localdir):
628 5
        if file.endswith(".bar"):
629 5
            print("TESTING: {0}".format(file))
630 5
            signname, signhash = barutils.retrieve_sha512(os.path.join(localdir, file))
631 5
            sha512ver = barutils.verify_sha512(os.path.join(localdir, signname.decode("utf-8")), signhash)
632 5
            if not sha512ver:
633 5
                print("{0} IS BROKEN".format((file)))
634 5
                break
635
    else:
636 5
        print("ALL FILES EXTRACTED OK")
637
638
639 5
def test_loader_files(localdir):
640
    """
641
    Test loader files after creation.
642
643
    :param localdir: Directory.
644
    :type localdir: str
645
    """
646 5
    if not utilities.is_windows():
647 5
        pass
648
    else:
649 5
        print("TESTING LOADER FILES...")
650 5
        brokens = utilities.verify_bulk_loaders(localdir)
651 5
        if brokens:
652 5
            print("BROKEN FILES:")
653 5
            utilities.lprint(brokens)
654 5
            raise SystemExit
655
        else:
656 5
            print("ALL FILES CREATED OK")
657
658
659 5
def test_single_loader(loaderfile):
660
    """
661
    Test single loader file after creation.
662
663
    :param loaderfile: File to check.
664
    :type loaderfile: str
665
    """
666 5
    if not utilities.is_windows():
667 5
        pass
668
    else:
669 5
        print("TESTING LOADER...")
670 5
        if not utilities.verify_loader_integrity(loaderfile):
671 5
            print("{0} IS BROKEN!".format(os.path.basename(loaderfile)))
672 5
            raise SystemExit
673
        else:
674 5
            print("LOADER CREATED OK")
675
676
677 5
def prod_avail(results, mailer=False, osversion=None, password=None):
678
    """
679
    Clean availability for production lookups for autolookup script.
680
681
    :param results: Result dict.
682
    :type results: dict(str: str)
683
684
    :param mailer: If we're mailing links. Default is false.
685
    :type mailer: bool
686
687
    :param osversion: OS version.
688
    :type osversion: str
689
690
    :param password: Email password.
691
    :type password: str
692
    """
693 5
    prel = results['p']
694 5
    if prel != "SR not in system" and prel is not None:
695 5
        pav = "PD"
696 5
        baseurl = utilities.create_base_url(prel)
697 5
        avail = networkutils.availability(baseurl)
698 5
        is_avail = "Available" if avail else "Unavailable"
699 5
        prod_avail_mailprep(prel, avail, osversion, mailer, password)
700
    else:
701 5
        pav = "  "
702 5
        is_avail = "Unavailable"
703 5
    return prel, pav, is_avail
704
705
706 5
def prod_avail_mailprep(prel, avail, osversion=None, mailer=False, password=None):
707
    """
708
    Do SQL/SMTP prep work after a good production lookup hit.
709
710
    :param prel: Software lookup result.
711
    :type prel: str
712
713
    :param avail: If software lookup result is available for download.
714
    :type avail: bool
715
716
    :param osversion: OS version.
717
    :type osversion: str
718
719
    :param mailer: If we're mailing links. Default is false.
720
    :type mailer: bool
721
722
    :param password: Email password.
723
    :type password: str
724
    """
725 5
    if avail and mailer:
726 5
        sqlutils.prepare_sw_db()
727 5
        if not sqlutils.check_exists(osversion, prel):
728 5
            rad = utilities.increment(osversion, 1)
729 5
            linkgen(osversion, rad, prel, temp=True)
730 5
            smtputils.prep_email(osversion, prel, password)
731
732
733 5
def comp_joiner(rootdir, localdir, filelist):
734
    """
735
    Join rootdir, localdir to every file in filelist.
736
737
    :param rootdir: Root directory.
738
    :type rootdir: str
739
740
    :param localdir: Subfolder inside rootdir.
741
    :type localdir: str
742
743
    :param filelist: List of files to return this path for.
744
    :type filelist: list(str)
745
    """
746 5
    joinedfiles = [os.path.join(rootdir, localdir, os.path.basename(x)) for x in filelist]
747 5
    return joinedfiles
748
749
750 5
def kernchecker_prep(kernlist):
751
    """
752
    Prepare output from kernel list.
753
754
    :param kernlist: List of kernel branches.
755
    :type kernlist: list(str)
756
    """
757 5
    splitkerns = [x.split("/") for x in kernlist]
758 5
    platforms = list({x[0] for x in splitkerns})
759 5
    kerndict = kernchecker_dict(splitkerns, platforms)
760 5
    return kerndict
761
762
763 5
def kernchecker_dict(splitkerns, platforms):
764
    """
765
    Prepare results dictionary.
766
767
    :param splitkerns: Split kernel branches.
768
    :type splitkerns: list(str)
769
770
    :param platforms: List of platform dicts.
771
    :type platforms: list(dict)
772
    """
773 5
    kerndict = {x: [] for x in platforms}
774 5
    for kernel in splitkerns:
775 5
        kerndict[kernel[0]].append("\t{0}".format(kernel[1]))
776 5
    return kerndict
777
778
779 5
def tclloader_prep(loaderfile, directory=False):
780
    """
781
    Prepare directory name and OS version.
782
783
    :param loaderfile: Path to input file/folder.
784
    :type loaderfile: str
785
786
    :param directory: If the input file is a folder. Default is False.
787
    :type directory: bool
788
    """
789 5
    loaderdir = loaderfile if directory else loaderfile.replace(".zip", "")
790 5
    osver = loaderdir.split("-")[-1]
791 5
    return loaderdir, osver
792
793
794 5
def tclloader_filename(loaderdir, osver, loadername=None):
795
    """
796
    Prepare platform and filename.
797
798
    :param loaderdir: Path to input folder.
799
    :type loaderdir: str
800
801
    :param osver: OS version.
802
    :type osver: str
803
804
    :param loadername: Name of final autoloader. Default is auto-generated.
805
    :type loadername: str
806
    """
807 5
    platform = os.listdir(os.path.join(loaderdir, "target", "product"))[0]
808 5
    if loadername is None:
809 5
        loadername = "{0}_autoloader_user-all-{1}".format(platform, osver)
810 5
    return loadername, platform
811
812
813 5
def tcl_download(downloadurl, filename, filesize, filehash):
814
    """
815
    Download autoloader file, rename, and verify.
816
817
    :param downloadurl: Download URL.
818
    :type downloadurl: str
819
820
    :param filename: Name of autoloader file.
821
    :type filename: str
822
823
    :param filesize: Size of autoloader file.
824
    :type filesize: str
825
826
    :param filehash: SHA-1 hash of autoloader file.
827
    :type filehash: str
828
    """
829 5
    print("FILENAME: {0}".format(filename))
830 5
    print("LENGTH: {0}".format(utilities.fsizer(filesize)))
831 5
    networkutils.download(downloadurl)
832 5
    print("DOWNLOAD COMPLETE")
833 5
    os.rename(downloadurl.split("/")[-1], filename)
834 5
    method = hashutils.get_engine("sha1")
835 5
    shahash = hashutils.hashlib_hash(filename, method)
836 5
    if shahash == filehash:
837 5
        print("HASH CHECK OK")
838
    else:
839 5
        print(shahash)
840 5
        print("HASH FAILED!")
841
842
843 5
def tcl_prd_scan(curef, download=False, mode=4, fvver="AAA000", original=True):
844
    """
845
    Scan one PRD and produce download URL and filename.
846
847
    :param curef: PRD of the phone variant to check.
848
    :type curef: str
849
850
    :param download: If we'll download the file that this returns. Default is False.
851
    :type download: bool
852
853
    :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas.
854
    :type mode: int
855
856
    :param fvver: Initial software version, must be specific if downloading OTA deltas.
857
    :type fvver: str
858
859
    :param original: If we'll download the file with its original filename instead of delta-safe. Default is True.
860
    :type original: bool
861
    """
862 5
    sess = requests.Session()
863 5
    ctext = networkutils.tcl_check(curef, sess, mode, fvver)
864 5
    if ctext is None:
865 5
        raise SystemExit
866 5
    tvver, firmwareid, filename, filesize, filehash = networkutils.parse_tcl_check(ctext)
867 5
    salt = networkutils.tcl_salt()
868 5
    vkhsh = networkutils.vkhash(curef, tvver, firmwareid, salt, mode, fvver)
869 5
    updatetext = networkutils.tcl_download_request(curef, tvver, firmwareid, salt, vkhsh, sess, mode, fvver)
870 5
    downloadurl, encslave = networkutils.parse_tcl_download_request(updatetext)
871 5
    statcode = networkutils.getcode(downloadurl, sess)
872 5
    filename = tcl_delta_filename(curef, fvver, tvver, filename, original)
873 5
    tcl_prd_print(downloadurl, filename, statcode, encslave, sess)
874 5
    if statcode == 200 and download:
875 5
        tcl_download(downloadurl, filename, filesize, filehash)
876
877
878 5
def tcl_delta_filename(curef, fvver, tvver, filename, original=True):
879
    """
880
    Generate compatible filenames for deltas, if needed.
881
882
    :param curef: PRD of the phone variant to check.
883
    :type curef: str
884
885
    :param fvver: Initial software version.
886
    :type fvver: str
887
888
    :param tvver: Target software version.
889
    :type tvver: str
890
891
    :param filename: File name from download URL, passed through if not changing filename.
892
    :type filename: str
893
894
    :param original: If we'll download the file with its original filename instead of delta-safe. Default is True.
895
    :type original: bool
896
    """
897 5
    if not original:
898 5
        prdver = curef.split("-")[1]
899 5
        filename = "JSU_PRD-{0}-{1}to{2}.zip".format(prdver, fvver, tvver)
900 5
    return filename
901
902
903 5
def tcl_prd_print(downloadurl, filename, statcode, encslave, session):
904
    """
905
    Print output from PRD scanning.
906
907
    :param downloadurl: File to download.
908
    :type downloadurl: str
909
910
    :param filename: File name from download URL.
911
    :type filename: str
912
913
    :param statcode: Status code of download URL.
914
    :type statcode: int
915
916
    :param encslave: Server hosting header script.
917
    :type encslave: str
918
919
    :param session: Session object.
920
    :type session: requests.Session
921
    """
922 5
    print("{0}: HTTP {1}".format(filename, statcode))
923 5
    print(downloadurl)
924 5
    if encslave is not None:
925 5
        address = "/{0}".format(downloadurl.split("/", 3)[3:][0])
926 5
        print("CHECKING HEADER...")
927 5
        sentinel = networkutils.encrypt_header(address, encslave, session)
928 5
        if sentinel is not None:
929 5
            print(sentinel)
930
931
932 5
def tcl_prep_otaver(ota=None):
933
    """
934
    Prepare variables for OTA versus full check.
935
936
    :param ota: The starting version if we're checking OTA updates, None if we're not. Default is None.
937
    :type ota: str
938
    """
939 5
    if ota is not None:
940 5
        mode = 2
941 5
        fvver = ota
942
    else:
943 5
        mode = 4
944 5
        fvver = "AAA000"
945 5
    return mode, fvver
946
947
948 5
def tcl_mainscan_preamble(ota=None):
949
    """
950
    Prepare preamble for TCL scanning.
951
952
    :param ota: The starting version if we're checking OTA updates, None if we're not. Default is None.
953
    :type ota: str
954
    """
955 5
    slim_preamble("TCLSCAN")
956 5
    if ota is not None:
957 5
        print("PRDs with OTA from OS {0}".format(ota.upper()))
958
959
960 5
def tcl_mainscan_printer(curef, tvver, ota=None):
961
    """
962
    Print output of TCL scanning.
963
964
    :param curef: PRD of the phone variant to check.
965
    :type curef: str
966
967
    :param tvver: Target software version.
968
    :type tvver: str
969
970
    :param ota: The starting version if we're checking OTA updates, None if we're not. Default is None.
971
    :type ota: str
972
    """
973 5
    if ota is not None:
974
        print("{0}: {2} to {1}".format(curef, tvver, ota.upper()))
975
    else:
976 5
        print("{0}: {1}".format(curef, tvver))
977
978
979
def linkgen_sdk_dicter(indict, origtext, newtext):
980
    """
981
    Prepare SDK radio/OS dictionaries.
982
983
    :param indict: Dictionary of radio and OS pairs.
984
    :type: dict(str:str)
985
986
    :param origtext: String in indict's values that must be replaced.
987
    :type origtext: str
988
989 5
    :param newtext: What to replace origtext with.
990 5
    :type newtext: str
991 5
    """
992 5
    return {key: val.replace(origtext, newtext) for key, val in indict.items()}
993 5
994 5
995
def linkgen_sdk(sdk, oses, cores):
996
    """
997 5
    Generate SDK debrick/core images.
998
999
    :param sdk: If we specifically want SDK images. Default is False.
1000
    :type sdk: bool
1001
1002
    :param oses: Dictionary of radio and debrick pairs.
1003
    :type oses: dict(str:str)
1004
1005
    :param cores: Dictionary of radio and core pairs.
1006
    :type cores: dict(str:str)
1007
    """
1008
    if sdk:
1009
        oses2 = linkgen_sdk_dicter(oses, "factory_sfi", "sdk")
1010
        cores2 = linkgen_sdk_dicter(cores, "factory_sfi", "sdk")
1011
        oses = linkgen_sdk_dicter(oses2, "verizon_sfi", "sdk")
1012
        cores = linkgen_sdk_dicter(cores2, "verizon_sfi", "sdk")
1013
    return oses, cores
1014
1015
1016
def linkgen(osversion, radioversion=None, softwareversion=None, altsw=None, temp=False, sdk=False):
1017
    """
1018
    Generate debrick/core/radio links for given OS, radio, software release.
1019 5
1020 5
    :param osversion: OS version, 10.x.y.zzzz.
1021 5
    :type osversion: str
1022 5
1023 5
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
1024 5
    :type radioversion: str
1025 5
1026 5
    :param softwareversion: Software version, 10.x.y.zzzz. Can be guessed.
1027 5
    :type softwareversion: str
1028 5
1029 5
    :param altsw: Radio software release, if not the same as OS.
1030 5
    :type altsw: str
1031 5
1032 5
    :param temp: If file we write to is temporary. Default is False.
1033 5
    :type temp: bool
1034 5
1035 5
    :param sdk: If we specifically want SDK images. Default is False.
1036 5
    :type sdk: bool
1037
    """
1038
    radioversion = return_radio_version(osversion, radioversion)
1039 5
    softwareversion, swc = return_sw_checked(softwareversion, osversion)
1040
    del swc
1041
    if altsw is not None:
1042
        altsw, aswc = return_radio_sw_checked(altsw, radioversion)
1043
        del aswc
1044
    baseurl = utilities.create_base_url(softwareversion)
1045
    oses, cores, radios = textgenerator.url_gen(osversion, radioversion, softwareversion)
1046 5
    if altsw is not None:
1047 5
        del radios
1048 5
        dbks, cors, radios = textgenerator.url_gen(osversion, radioversion, altsw)
1049 5
        del dbks
1050
        del cors
1051 5
    avlty = networkutils.availability(baseurl)
1052 5
    oses, cores = linkgen_sdk(sdk, oses, cores)
1053
    prargs = (softwareversion, osversion, radioversion, oses, cores, radios, avlty, False, None, temp, altsw)
1054
    lthr = threading.Thread(target=textgenerator.write_links, args=prargs)
1055 5
    lthr.start()
1056
1057
1058
def clean_swrel(swrelset):
1059
    """
1060
    Clean a list of software release lookups.
1061
1062
    :param swrelset: List of software releases.
1063
    :type swrelset: set(str)
1064
    """
1065 5
    for i in swrelset:
1066 5
        if i != "SR not in system" and i is not None:
1067
            swrelease = i
1068
            break
1069 5
    else:
1070
        swrelease = ""
1071
    return swrelease
1072
1073
1074
def autolookup_logger(record, out):
1075
    """
1076
    Write autolookup results to file.
1077
1078
    :param record: The file to log to.
1079
    :type record: str
1080
1081
    :param out: Output block.
1082
    :type out: str
1083
    """
1084
    with open(record, "a") as rec:
1085
        rec.write("{0}\n".format(out))
1086
1087
1088 5
def autolookup_printer(out, avail, log=False, quiet=False, record=None):
1089 5
    """
1090 5
    Print autolookup results, logging if specified.
1091 5
1092 5
    :param out: Output block.
1093 5
    :type out: str
1094 5
1095
    :param avail: Availability. Can be "Available" or "Unavailable".
1096
    :type avail: str
1097 5
1098
    :param log: If we're logging to file.
1099
    :type log: bool
1100
1101
    :param quiet: If we only note available entries.
1102
    :type quiet: bool
1103
1104
    :param record: If we're logging, the file to log to.
1105
    :type record: str
1106
    """
1107
    if not quiet:
1108
        avail = "Available"  # force things
1109
    if avail.lower() == "available":
1110
        if log:
1111
            lthr = threading.Thread(target=autolookup_logger, args=(record, out))
1112
            lthr.start()
1113 5
        print(out)
1114 5
1115 5
1116 5
def autolookup_output_sql(osversion, swrelease, avail, sql=False):
1117
    """
1118
    Add OS to SQL database.
1119 5
1120
    :param osversion: OS version.
1121
    :type osversion: str
1122
1123
    :param swrelease: Software release.
1124
    :type swrelease: str
1125
1126
    :param avail: "Unavailable" or "Available".
1127
    :type avail: str
1128
1129
    :param sql: If we're adding this to our SQL database.
1130
    :type sql: bool
1131
    """
1132
    if sql:
1133
        sqlutils.prepare_sw_db()
1134
        if not sqlutils.check_exists(osversion, swrelease):
1135
            sqlutils.insert(osversion, swrelease, avail.lower())
1136
1137
1138 5
def autolookup_output(osversion, swrelease, avail, avpack, sql=False):
1139 5
    """
1140 5
    Prepare autolookup block, and add to SQL database.
1141 5
1142 5
    :param osversion: OS version.
1143
    :type osversion: str
1144
1145 5
    :param swrelease: Software release.
1146
    :type swrelease: str
1147
1148
    :param avail: "Unavailable" or "Available".
1149
    :type avail: str
1150
1151
    :param avpack: Availabilities: alpha 1 and 2, beta 1 and 2, production.
1152
    :type avpack: list(str)
1153
1154
    :param sql: If we're adding this to our SQL database.
1155 5
    :type sql: bool
1156 5
    """
1157
    othr = threading.Thread(target=autolookup_output_sql, args=(osversion, swrelease, avail, sql))
1158
    othr.start()
1159 5
    avblok = "[{0}|{1}|{2}|{3}|{4}]".format(*avpack)
1160
    out = "OS {0} - SR {1} - {2} - {3}".format(osversion, swrelease, avblok, avail)
1161
    return out
1162
1163
1164
def clean_barlist(cleanfiles, stoppers):
1165
    """
1166
    Remove certain bars from barlist based on keywords.
1167
1168
    :param cleanfiles: List of files to clean.
1169
    :type cleanfiles: list(str)
1170
1171
    :param stoppers: List of keywords (i.e. bar names) to exclude.
1172
    :type stoppers: list(str)
1173
    """
1174
    finals = [link for link in cleanfiles if all(word not in link for word in stoppers)]
1175
    return finals
1176
1177
1178
def prep_export_cchecker(files, npc, hwid, osv, radv, swv, upgrade=False, forced=None):
1179
    """
1180
    Prepare carrierchecker lookup links to write to file.
1181
1182
    :param files: List of file URLs.
1183
    :type files: list(str)
1184
1185
    :param npc: MCC + MNC (ex. 302220).
1186
    :type npc: int
1187 5
1188 5
    :param hwid: Device hardware ID.
1189 5
    :type hwid: str
1190
1191 5
    :param osv: OS version.
1192 5
    :type osv: str
1193 5
1194 5
    :param radv: Radio version.
1195 5
    :type radv: str
1196
1197
    :param swv: Software release.
1198 5
    :type swv: str
1199
1200
    :param upgrade: Whether or not to use upgrade files. Default is false.
1201
    :type upgrade: bool
1202
1203
    :param forced: Force a software release. None to go for latest.
1204
    :type forced: str
1205
    """
1206
    if not upgrade:
1207
        newfiles = networkutils.carrier_query(npc, hwid, True, False, forced)
1208
        cleanfiles = newfiles[3]
1209
    else:
1210
        cleanfiles = files
1211
    osurls, coreurls, radiourls = textgenerator.url_gen(osv, radv, swv)
1212
    stoppers = ["8960", "8930", "8974", "m5730", "winchester"]
1213
    finals = clean_barlist(cleanfiles, stoppers)
1214
    return osurls, coreurls, radiourls, finals
1215
1216
1217
def export_cchecker(files, npc, hwid, osv, radv, swv, upgrade=False, forced=None):
1218
    """
1219
    Write carrierchecker lookup links to file.
1220
1221
    :param files: List of file URLs.
1222
    :type files: list(str)
1223
1224
    :param npc: MCC + MNC (ex. 302220).
1225
    :type npc: int
1226 5
1227 5
    :param hwid: Device hardware ID.
1228 5
    :type hwid: str
1229 5
1230
    :param osv: OS version.
1231 5
    :type osv: str
1232
1233
    :param radv: Radio version.
1234 5
    :type radv: str
1235
1236
    :param swv: Software release.
1237
    :type swv: str
1238
1239
    :param upgrade: Whether or not to use upgrade files. Default is false.
1240
    :type upgrade: bool
1241
1242
    :param forced: Force a software release. None to go for latest.
1243
    :type forced: str
1244
    """
1245
    if files:
1246
        osurls, coreurls, radiourls, finals = prep_export_cchecker(files, npc, hwid, osv, radv, swv, upgrade, forced)
1247
        textgenerator.write_links(swv, osv, radv, osurls, coreurls, radiourls, True, True, finals)
1248
        print("\nFINISHED!!!")
1249 5
    else:
1250
        print("CANNOT EXPORT, NO SOFTWARE RELEASE")
1251
1252
1253
def generate_blitz_links(files, osv, radv, swv):
1254
    """
1255 5
    Generate blitz URLs (i.e. all OS and radio links).
1256
    :param files: List of file URLs.
1257
    :type files: list(str)
1258
1259
    :param osv: OS version.
1260
    :type osv: str
1261
1262
    :param radv: Radio version.
1263 5
    :type radv: str
1264
1265
    :param swv: Software release.
1266 5
    :type swv: str
1267
    """
1268
    coreurls = [
1269
        utilities.create_bar_url(swv, "winchester.factory_sfi", osv),
1270
        utilities.create_bar_url(swv, "qc8960.factory_sfi", osv),
1271
        utilities.create_bar_url(swv, "qc8960.factory_sfi", osv),
1272
        utilities.create_bar_url(swv, "qc8960.factory_sfi_hybrid_qc8974", osv)
1273
    ]
1274
    radiourls = [
1275
        utilities.create_bar_url(swv, "m5730", radv),
1276 5
        utilities.create_bar_url(swv, "qc8960", radv),
1277 5
        utilities.create_bar_url(swv, "qc8960.wtr", radv),
1278 5
        utilities.create_bar_url(swv, "qc8960.wtr5", radv),
1279 5
        utilities.create_bar_url(swv, "qc8930.wtr5", radv),
1280 5
        utilities.create_bar_url(swv, "qc8974.wtr2", radv)
1281 5
    ]
1282 5
    return files + coreurls + radiourls
1283
1284 5
1285
def package_blitz(bardir, swv):
1286
    """
1287 5
    Package and verify a blitz package.
1288
1289
    :param bardir: Path to folder containing bar files.
1290
    :type bardir: str
1291
1292
    :param swv: Software version.
1293
    :type swv: str
1294 5
    """
1295
    print("\nCREATING BLITZ...")
1296
    barutils.create_blitz(bardir, swv)
1297 5
    print("\nTESTING BLITZ...")
1298
    zipver = archiveutils.zip_verify("Blitz-{0}.zip".format(swv))
1299
    if not zipver:
1300
        print("BLITZ FILE IS BROKEN")
1301
        raise SystemExit
1302
    else:
1303
        shutil.rmtree(bardir)
1304
1305
1306
def slim_preamble(appname):
1307
    """
1308
    Standard app name header.
1309
1310
    :param appname: Name of app.
1311
    :type appname: str
1312
    """
1313
    print("~~~{0} VERSION {1}~~~".format(appname.upper(), shortversion()))
1314
1315
1316 5
def standard_preamble(appname, osversion, softwareversion, radioversion, altsw=None):
1317 5
    """
1318 5
    Standard app name, OS, radio and software (plus optional radio software) print block.
1319 5
1320 5
    :param appname: Name of app.
1321 5
    :type appname: str
1322
1323
    :param osversion: OS version, 10.x.y.zzzz. Required.
1324 5
    :type osversion: str
1325
1326
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
1327
    :type radioversion: str
1328 5
1329 5
    :param softwareversion: Software release, 10.x.y.zzzz. Can be guessed.
1330 5
    :type softwareversion: str
1331 5
1332 5
    :param altsw: Radio software release, if not the same as OS.
1333 5
    :type altsw: str
1334 5
    """
1335 5
    slim_preamble(appname)
1336
    print("OS VERSION: {0}".format(osversion))
1337
    print("OS SOFTWARE VERSION: {0}".format(softwareversion))
1338 5
    print("RADIO VERSION: {0}".format(radioversion))
1339
    if altsw is not None:
1340
        print("RADIO SOFTWARE VERSION: {0}".format(altsw))
1341
1342 5
1343 5
def questionnaire_device(message=None):
1344 5
    """
1345 5
    Get device from questionnaire.
1346 5
    """
1347 5
    message = "DEVICE (XXX100-#): " if message is None else message
1348 5
    device = input(message)
1349 5
    if not device:
1350 5
        print("NO DEVICE SPECIFIED!")
1351
        decorators.enter_to_exit(True)
1352 5
        if not getattr(sys, 'frozen', False):
1353 5
            raise SystemExit
1354
    return device
1355
1356 5
1357
def verify_gpg_credentials():
1358
    """
1359
    Read GPG key/pass from file, verify if incomplete.
1360
    """
1361
    gpgkey, gpgpass = gpgutils.gpg_config_loader()
1362
    if gpgkey is None or gpgpass is None:
1363 5
        print("NO PGP KEY/PASS FOUND")
1364 5
        cont = utilities.s2b(input("CONTINUE (Y/N)?: "))
1365 5
        if cont:
1366 5
            gpgkey = verify_gpg_key(gpgkey)
1367 5
            gpgpass, writebool = verify_gpg_pass(gpgpass)
1368
            gpgpass2 = gpgpass if writebool else None
1369
            gpgutils.gpg_config_writer(gpgkey, gpgpass2)
1370 5
        else:
1371
            gpgkey = None
1372
    return gpgkey, gpgpass
1373
1374
1375
def verify_gpg_key(gpgkey=None):
1376
    """
1377 5
    Verify GPG key.
1378 5
1379 5
    :param gpgkey: Key, use None to take from input.
1380
    :type gpgkey: str
1381 5
    """
1382 5
    if gpgkey is None:
1383
        gpgkey = input("PGP KEY (0x12345678): ")
1384
        if not gpgkey.startswith("0x"):
1385 5
            gpgkey = "0x{0}".format(gpgkey)   # add preceding 0x
1386
    return gpgkey
1387
1388
1389
def verify_gpg_pass(gpgpass=None):
1390
    """
1391
    Verify GPG passphrase.
1392
1393
    :param gpgpass: Passphrase, use None to take from input.
1394
    :type gpgpass: str
1395
    """
1396
    if gpgpass is None:
1397
        gpgpass = getpass.getpass(prompt="PGP PASSPHRASE: ")
1398
        writebool = utilities.s2b(input("SAVE PASSPHRASE (Y/N)?:"))
1399
    else:
1400
        writebool = False
1401
    return gpgpass, writebool
1402
1403
1404 5
def bulk_hash(dirs, compressed=True, deleted=True, radios=True, hashdict=None):
1405 5
    """
1406
    Hash files in several folders based on flags.
1407
1408 5
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
1409
    :type dirs: list(str)
1410
1411
    :param compressed: Whether to hash compressed files. True by default.
1412
    :type compressed: bool
1413
1414
    :param deleted: Whether to delete uncompressed files. True by default.
1415
    :type deleted: bool
1416
1417
    :param radios: Whether to hash radio autoloaders. True by default.
1418
    :type radios: bool
1419
1420
    :param hashdict: Dictionary of hash rules, in ~\bbarchivist.ini.
1421
    :type hashdict: dict({str: bool})
1422
    """
1423
    print("HASHING LOADERS...")
1424 5
    utilities.cond_check(hashutils.verifier, utilities.def_args(dirs), [hashdict], radios, compressed, deleted)
1425 5
1426 5
1427 5
def bulk_verify(dirs, compressed=True, deleted=True, radios=True):
1428 5
    """
1429 5
    Verify files in several folders based on flags.
1430
1431
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
1432 5
    :type dirs: list(str)
1433
1434
    :param compressed: Whether to hash compressed files. True by default.
1435
    :type compressed: bool
1436
1437
    :param deleted: Whether to delete uncompressed files. True by default.
1438
    :type deleted: bool
1439 5
1440
    :param radios: Whether to hash radio autoloaders. True by default.
1441
    :type radios: bool
1442 5
    """
1443
    gpgkey, gpgpass = verify_gpg_credentials()
1444
    if gpgpass is not None and gpgkey is not None:
1445
        print("VERIFYING LOADERS...")
1446
        print("KEY: {0}".format(gpgkey))
1447
        restargs = [gpgkey, gpgpass, True]
1448
        utilities.cond_check(gpgutils.gpgrunner, utilities.def_args(dirs), restargs, radios, compressed, deleted)
1449 5
1450 5
1451 5
def enn_ayy(quant):
1452 5
    """
1453
    Cheeky way to put a N/A placeholder for a string.
1454
1455 5
    :param quant: What to check if it's None.
1456
    :type quant: str
1457
    """
1458
    return "N/A" if quant is None else quant
1459
1460
1461
def generate_workfolder(folder=None):
1462
    """
1463
    Check if a folder exists, make it if it doesn't, set it to home if None.
1464
1465
    :param folder: Folder to check.
1466
    :type folder: str
1467
    """
1468
    folder = utilities.dirhandler(folder, os.getcwd())
1469
    if folder is not None and not os.path.exists(folder):
1470
        os.makedirs(folder)
1471
    return folder
1472
1473
1474 5
def info_header(afile, osver, radio=None, software=None, device=None):
1475 5
    """
1476 5
    Write header for info file.
1477
1478 5
    :param afile: Open file to write to.
1479 5
    :type afile: File object
1480 5
1481
    :param osver: OS version, required for both types.
1482
    :type osver: str
1483 5
1484
    :param radio: Radio version, required for QNX.
1485
    :type radio: str
1486
1487
    :param software: Software release, required for QNX.
1488
    :type software: str
1489
1490
    :param device: Device type, required for Android.
1491
    :type device: str
1492
    """
1493
    afile.write("OS: {0}\n".format(osver))
1494
    if device:
1495
        afile.write("Device: {0}\n".format(enn_ayy(device)))
1496 5
    else:
1497 5
        afile.write("Radio: {0}\n".format(enn_ayy(radio)))
1498 5
        afile.write("Software: {0}\n".format(enn_ayy(software)))
1499 5
    afile.write("{0}\n".format("~"*40))
1500 5
1501
1502
def prep_info(filepath, osver, device=None):
1503 5
    """
1504
    Prepare file list for new-style info file.
1505
1506
    :param filepath: Path to folder to analyze.
1507
    :type filepath: str
1508
1509
    :param osver: OS version, required for both types.
1510
    :type osver: str
1511
1512
    :param device: Device type, required for Android.
1513
    :type device: str
1514
    """
1515
    fileext = ".zip" if device else ".7z"
1516
    files = os.listdir(filepath)
1517
    absfiles = [os.path.join(filepath, x) for x in files if x.endswith((fileext, ".exe"))]
1518
    fname = os.path.join(filepath, "!{0}_OSINFO!.txt".format(osver))
1519
    return fname, absfiles
1520
1521
1522 5
def make_info(filepath, osver, radio=None, software=None, device=None):
1523 5
    """
1524 5
    Create a new-style info (names, sizes and hashes) file.
1525 5
1526 5
    :param filepath: Path to folder to analyze.
1527
    :type filepath: str
1528
1529 5
    :param osver: OS version, required for both types.
1530
    :type osver: str
1531
1532
    :param radio: Radio version, required for QNX.
1533
    :type radio: str
1534
1535
    :param software: Software release, required for QNX.
1536
    :type software: str
1537
1538
    :param device: Device type, required for Android.
1539
    :type device: str
1540
    """
1541
    fname, absfiles = prep_info(filepath, osver, device)
1542
    with open(fname, "w") as afile:
1543
        info_header(afile, osver, radio, software, device)
1544
        for indx, file in enumerate(absfiles):
1545 5
            write_info(file, indx, len(absfiles), afile)
1546 5
1547 5
1548 5
def write_info(infile, index, filecount, outfile):
1549 5
    """
1550 5
    Write a new-style info (names, sizes and hashes) file.
1551 5
1552 5
    :param infile: Path to file whose name, size and hash are to be written.
1553 5
    :type infile: str
1554 5
1555
    :param index: Which file index out of the list of files we're writing.
1556
    :type index: int
1557 5
1558
    :param filecount: Total number of files we're to write; for excluding terminal newline.
1559
    :type filecount: int
1560
1561
    :param outfile: Open (!!!) file handle. Output file.
1562
    :type outfile: str
1563
    """
1564
    fsize = os.stat(infile).st_size
1565
    outfile.write("File: {0}\n".format(os.path.basename(infile)))
1566
    outfile.write("\tSize: {0} ({1})\n".format(fsize, utilities.fsizer(fsize)))
1567
    outfile.write("\tHashes:\n")
1568
    outfile.write("\t\tMD5: {0}\n".format(hashutils.hashlib_hash(infile, hashlib.md5()).upper()))
1569
    outfile.write("\t\tSHA1: {0}\n".format(hashutils.hashlib_hash(infile, hashlib.sha1()).upper()))
1570
    outfile.write("\t\tSHA256: {0}\n".format(hashutils.hashlib_hash(infile, hashlib.sha256()).upper()))
1571
    outfile.write("\t\tSHA512: {0}\n".format(hashutils.hashlib_hash(infile, hashlib.sha512()).upper()))
1572
    if index != filecount - 1:
1573
        outfile.write("\n")
1574
1575
1576
def bulk_info(dirs, osv, compressed=True, deleted=True, radios=True, rad=None, swv=None, dev=None):
1577
    """
1578
    Generate info files in several folders based on flags.
1579
1580
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
1581
    :type dirs: list(str)
1582
1583
    :param osver: OS version, required for both types.
1584
    :type osver: str
1585 5
1586 5
    :param compressed: Whether to hash compressed files. True by default.
1587 5
    :type compressed: bool
1588
1589
    :param deleted: Whether to delete uncompressed files. True by default.
1590
    :type deleted: bool
1591
1592
    :param radios: Whether to hash radio autoloaders. True by default.
1593
    :type radios: bool
1594
1595
    :param rad: Radio version, required for QNX.
1596
    :type rad: str
1597
1598
    :param swv: Software release, required for QNX.
1599
    :type swv: str
1600
1601
    :param dev: Device type, required for Android.
1602
    :type dev: str
1603
    """
1604
    print("GENERATING INFO FILES...")
1605
    restargs = [osv, rad, swv, dev]
1606
    utilities.cond_check(make_info, utilities.def_args(dirs), restargs, radios, compressed, deleted)
1607