Completed
Push — master ( 831ce5...144d0f )
by John
03:50
created

tcl_mainscan_preamble()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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