Passed
Push — master ( 604dcd...57fbcd )
by John
03:24
created

bbarchivist.scriptutils.check_os_bulk_handle()   A

Complexity

Conditions 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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