Passed
Push — master ( a829b5...274f62 )
by John
02:16
created

bbarchivist.scriptutils.export_cchecker()   B

Complexity

Conditions 2

Size

Total Lines 34
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 8
dl 0
loc 34
rs 8.8571
c 0
b 0
f 0
ccs 6
cts 6
cp 1
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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