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

bbarchivist.scriptutils.prep_export_cchecker()   B

Complexity

Conditions 2

Size

Total Lines 37
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 9
nop 8
dl 0
loc 37
rs 8.8571
c 0
b 0
f 0
ccs 9
cts 9
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 (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