Completed
Push — master ( ee956e...fc3a5e )
by John
02:41
created

generate_workfolder()   A

Complexity

Conditions 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
ccs 6
cts 6
cp 1
crap 4
1
#!/usr/bin/env python3
2 4
"""This module contains various utilities for the scripts folder."""
3
4 4
import os  # path work
5 4
import getpass  # invisible password
6 4
import argparse  # generic parser
7 4
import sys  # getattr
8 4
import shutil  # folder removal
9 4
import subprocess  # running cfp/cap
10 4
import glob  # file lookup
11 4
import threading  # run stuff in background
12 4
import requests  # session
13 4
from bbarchivist import utilities  # little things
14 4
from bbarchivist import decorators  # decorating functions
15 4
from bbarchivist import barutils  # file system work
16 4
from bbarchivist import archiveutils  # archive support
17 4
from bbarchivist import bbconstants  # constants
18 4
from bbarchivist import gpgutils  # gpg
19 4
from bbarchivist import hashutils  # file hashes
20 4
from bbarchivist import networkutils  # network tools
21 4
from bbarchivist import textgenerator  # writing text to file
22 4
from bbarchivist import smtputils  # email
23 4
from bbarchivist import sqlutils  # sql
24
25 4
__author__ = "Thurask"
26 4
__license__ = "WTFPL v2"
27 4
__copyright__ = "Copyright 2015-2016 Thurask"
28
29
30 4
def shortversion():
31
    """
32
    Get short app version (Git tag).
33
    """
34 4
    if not getattr(sys, 'frozen', False):
35 4
        ver = bbconstants.VERSION
36
    else:
37 4
        verfile = glob.glob(os.path.join(os.getcwd(), "version.txt"))[0]
38 4
        with open(verfile) as afile:
39 4
            ver = afile.read()
40 4
    return ver
41
42
43 4
def longversion():
44
    """
45
    Get long app version (Git tag + commits + hash).
46
    """
47 4
    if not getattr(sys, 'frozen', False):
48 4
        ver = (bbconstants.LONGVERSION, bbconstants.COMMITDATE)
49
    else:
50 4
        verfile = glob.glob(os.path.join(os.getcwd(), "longversion.txt"))[0]
51 4
        with open(verfile) as afile:
52 4
            ver = afile.read().split("\n")
53 4
    return ver
54
55
56 4
def default_parser(name=None, desc=None, flags=None, vers=None):
57
    """
58
    A generic form of argparse's ArgumentParser.
59
60
    :param name: App name.
61
    :type name: str
62
63
    :param desc: App description.
64
    :type desc: str
65
66
    :param flags: Tuple of sections to add.
67
    :type flags: tuple(str)
68
69
    :param vers: Versions: [git commit hash, git commit date]
70
    :param vers: list(str)
71
    """
72 4
    if vers is None:
73 4
        vers = longversion()
74 4
    parser = argparse.ArgumentParser(
75
        prog=name,
76
        description=desc,
77
        epilog="https://github.com/thurask/bbarchivist")
78 4
    parser.add_argument(
79
        "-v",
80
        "--version",
81
        action="version",
82
        version="{0} {1} committed {2}".format(parser.prog, vers[0], vers[1]))
83 4
    if flags is not None:
84 4
        if "folder" in flags:
85 4
            parser.add_argument(
86
                "-f",
87
                "--folder",
88
                dest="folder",
89
                help="Working folder",
90
                default=None,
91
                metavar="DIR",
92
                type=utilities.file_exists)
93 4
        if "osr" in flags:
94 4
            parser.add_argument(
95
                "os",
96
                help="OS version")
97 4
            parser.add_argument(
98
                "radio",
99
                help="Radio version, 10.x.y.zzzz",
100
                nargs="?",
101
                default=None)
102 4
            parser.add_argument(
103
                "swrelease",
104
                help="Software version, 10.x.y.zzzz",
105
                nargs="?",
106
                default=None)
107 4
    return parser
108
109
110 4
def generic_windows_shim(scriptname, scriptdesc, target, version):
111
    """
112
    Generic CFP/CAP runner; Windows only.
113
114 View Code Duplication
    :param scriptname: Script name, 'bb-something'.
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
115
    :type scriptname: str
116
117
    :param scriptdesc: Script description, i.e. scriptname -h.
118
    :type scriptdesc: str
119
120
    :param target: Path to file to execute.
121
    :type target: str
122
123
    :param version: Version of target.
124
    :type version: str
125
    """
126 4
    parser = default_parser(scriptname, scriptdesc)
127 4
    capver = "|{0}".format(version)
128 4
    parser = external_version(parser, capver)
129 4
    parser.parse_known_args(sys.argv[1:])
130 4
    if utilities.is_windows():
131 4
        subprocess.call([target] + sys.argv[1:])
132
    else:
133 4
        print("Sorry, Windows only.")
134
135
136 4
def external_version(parser, addition):
137
    """
138
    Modify the version string of argparse.ArgumentParser, adding something.
139
140
    :param parser: Parser to modify.
141
    :type parser: argparse.ArgumentParser
142
143 View Code Duplication
    :param addition: What to add.
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
144
    :type addition: str
145
    """
146 4
    verarg = [arg for arg in parser._actions if isinstance(arg, argparse._VersionAction)][0]
147 4
    verarg.version = "{1}{0}".format(addition, verarg.version)
148 4
    return parser
149
150
151 4
def return_radio_version(osversion, radioversion=None):
152
    """
153
    Increment radio version, if need be.
154
155
    :param osversion: OS version.
156
    :type osversion: str
157
158
    :param radioversion: Radio version, None if incremented.
159
    :type radioversion: str
160
    """
161 4
    if radioversion is None:
162 4
        radioversion = utilities.increment(osversion, 1)
163 4
    return radioversion
164
165
166 4
def return_sw_checked(softwareversion, osversion):
167
    """
168
    Check software existence, return boolean.
169
170
    :param softwareversion: Software release version.
171
    :type softwareversion: str
172
173
    :param osversion: OS version.
174
    :type osversion: str
175
    """
176 4
    if softwareversion is None:
177 4
        serv = bbconstants.SERVERS["p"]
178 4
        softwareversion = networkutils.sr_lookup(osversion, serv)
179 4
        if softwareversion == "SR not in system":
180 4
            print("SOFTWARE RELEASE NOT FOUND")
181 4
            cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
182 4
            if cont:
183 4
                softwareversion = input("SOFTWARE RELEASE: ")
184 4
                swchecked = False
185
            else:
186 4
                print("\nEXITING...")
187 4
                raise SystemExit  # bye bye
188
        else:
189 4
            swchecked = True
190
    else:
191 4
        swchecked = True
192 4
    return softwareversion, swchecked
193
194
195 4
def return_radio_sw_checked(altsw, radioversion):
196
    """
197
    Check radio software existence, return boolean.
198
199
    :param altsw: Software release version.
200
    :type altsw: str
201
202
    :param radioversion: Radio version.
203
    :type radioversion: str
204
    """
205 4
    if altsw == "checkme":
206 4
        serv = bbconstants.SERVERS["p"]
207 4
        testos = utilities.increment(radioversion, -1)
208 4
        altsw = networkutils.sr_lookup(testos, serv)
209 4
        if altsw == "SR not in system":
210 4
            print("RADIO SOFTWARE RELEASE NOT FOUND")
211 4
            cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
212 4
            if cont:
213 4
                altsw = input("SOFTWARE RELEASE: ")
214 4
                altchecked = False
215
            else:
216 4
                print("\nEXITING...")
217 4
                raise SystemExit  # bye bye
218
        else:
219 4
            altchecked = True
220
    else:
221 4
        altchecked = True
222 4
    return altsw, altchecked
223
224
225 4
def check_sw(baseurl, softwareversion, swchecked, altsw=False):
226
    """
227
    Check existence of software release.
228
229
    :param baseurl: Base URL (from http to hashed SW release).
230
    :type basurl: str
231
232
    :param softwareversion: Software release.
233
    :type softwareversion: str
234
235
    :param swchecked: If we checked the sw release already.
236
    :type swchecked: bool
237
238
    :param altsw: If this is the radio-only release. Default is false.
239
    :type altsw: bool
240
    """
241 4
    message = "CHECKING RADIO SOFTWARE RELEASE..." if altsw else "CHECKING SOFTWARE RELEASE..."
242 4
    print(message)
243 4
    if not swchecked:
244 4
        avlty = networkutils.availability(baseurl)
245 4
        if avlty:
246 4
            print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
247
        else:
248 4
            print("SOFTWARE RELEASE {0} NOT FOUND".format(softwareversion))
249 4
            cont = utilities.s2b(input("CONTINUE? Y/N: "))
250 4
            if not cont:
251 4
                print("\nEXITING...")
252 4
                raise SystemExit
253
    else:
254 4
        print("SOFTWARE RELEASE {0} EXISTS".format(softwareversion))
255
256
257 4
def check_radio_sw(alturl, altsw, altchecked):
258
    """
259
    Check existence of radio software release.
260
261
    :param alturl: Radio base URL (from http to hashed SW release).
262
    :type alturl: str
263
264
    :param altsw: Radio software release.
265
    :type altsw: str
266
267
    :param altchecked: If we checked the sw release already.
268
    :type altchecked: bool
269
    """
270 4
    return check_sw(alturl, altsw, altchecked, True)
271
272
273 4
def check_altsw(altcheck=False):
274
    """
275
    Ask for and return alternate software release, if needed.
276
277
    :param altcheck: If we're using an alternate software release.
278
    :type altcheck: bool
279
    """
280 4
    if altcheck:
281 4
        altsw = input("RADIO SOFTWARE RELEASE (PRESS ENTER TO GUESS): ")
282 4
        if not altsw:
283 4
            altsw = "checkme"
284
    else:
285 4
        altsw = None
286 4
    return altsw
287
288
289 4
def check_os_single(osurl, osversion, device):
290
    """
291
    Check existence of single OS link.
292
293
    :param radiourl: Radio URL to check.
294
    :type radiourl: str
295
296
    :param radioversion: Radio version.
297
    :type radioversion: str
298
299
    :param device: Device family.
300
    :type device: int
301
    """
302 4
    osav = networkutils.availability(osurl)
303 4
    if not osav:
304 4
        print("{0} NOT AVAILABLE FOR {1}".format(osversion, bbconstants.DEVICES[device]))
305 4
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
306 4
        if not cont:
307 4
            print("\nEXITING...")
308 4
            raise SystemExit
309
310
311 4
def check_os_bulk(osurls):
312
    """
313
    Check existence of list of OS links.
314
315
    :param osurls: OS URLs to check.
316
    :type osurls: list(str)
317
    """
318 4
    sess = requests.Session()
319 4
    for url in osurls:
320 4
        osav = networkutils.availability(url, sess)
321 4
        if osav:
322 4
            break
323
    else:
324 4
        print("OS VERSION NOT FOUND")
325 4
        cont = utilities.s2b(input("CONTINUE? Y/N: "))
326 4
        if not cont:
327 4
            print("\nEXITING...")
328 4
            raise SystemExit
329
330
331 4
def check_radio_single(radiourl, radioversion):
332
    """
333
    Check existence of single radio link.
334
335
    :param radiourl: Radio URL to check.
336
    :type radiourl: str
337
338
    :param radioversion: Radio version.
339
    :type radioversion: str
340
    """
341 4
    radav = networkutils.availability(radiourl)
342 4
    if not radav:
343 4
        print("RADIO VERSION NOT FOUND")
344 4
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
345 4
        if cont:
346 4
            rad2 = input("RADIO VERSION: ")
347 4
            radiourl = radiourl.replace(radioversion, rad2)
348 4
            radioversion = rad2
349
        else:
350 4
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
351 4
            if not going:
352 4
                print("\nEXITING...")
353 4
                raise SystemExit
354 4
    return radiourl, radioversion
355
356
357 4
def check_radio_bulk(radiourls, radioversion):
358
    """
359
    Check existence of list of radio links.
360
361
    :param radiourls: Radio URLs to check.
362
    :type radiourls: list(str)
363
364
    :param radioversion: Radio version.
365
    :type radioversion: str
366
    """
367 4
    sess = requests.Session()
368 4
    for url in radiourls:
369 4
        radav = networkutils.availability(url, sess)
370 4
        if radav:
371 4
            break
372
    else:
373 4
        print("RADIO VERSION NOT FOUND")
374 4
        cont = utilities.s2b(input("INPUT MANUALLY? Y/N: "))
375 4
        if cont:
376 4
            rad2 = input("RADIO VERSION: ")
377 4
            radiourls = [url.replace(radioversion, rad2) for url in radiourls]
378 4
            radioversion = rad2
379
        else:
380 4
            going = utilities.s2b(input("KEEP GOING? Y/N: "))
381 4
            if not going:
382 4
                print("\nEXITING...")
383 4
                raise SystemExit
384 4
    return radiourls, radioversion
385
386
387 4
def bulk_avail(urllist):
388
    """
389
    Filter 404 links out of URL list.
390
391
    :param urllist: URLs to check.
392
    :type urllist: list(str)
393
    """
394 4
    sess = requests.Session()
395 4
    url2 = [x for x in urllist if networkutils.availability(x, sess)]
396 4
    return url2
397
398
399 4
def get_baseurls(softwareversion, altsw=None):
400
    """
401
    Generate base URLs for bar links.
402
403
    :param softwareversion: Software version.
404
    :type softwareversion: str
405
406
    :param altsw: Radio software version, if necessary.
407
    :type altsw: str
408
    """
409 4
    baseurl = utilities.create_base_url(softwareversion)
410 4
    alturl = utilities.create_base_url(altsw) if altsw else None
411 4
    return baseurl, alturl
412
413
414 4
def get_sz_executable(compmethod):
415
    """
416
    Get 7z executable.
417
418
    :param compmethod: Compression method.
419
    :type compmethod: str
420
    """
421 4
    if compmethod != "7z":
422 4
        szexe = ""
423
    else:
424 4
        print("CHECKING PRESENCE OF 7ZIP...")
425 4
        psz = utilities.prep_seven_zip(True)
426 4
        if psz:
427 4
            print("7ZIP OK")
428 4
            szexe = utilities.get_seven_zip(False)
429
        else:
430 4
            szexe = ""
431 4
            print("7ZIP NOT FOUND")
432 4
            cont = utilities.s2b(input("CONTINUE? Y/N "))
433 4
            if cont:
434 4
                print("FALLING BACK TO ZIP...")
435 4
                compmethod = "zip"
436
            else:
437 4
                print("\nEXITING...")
438 4
                raise SystemExit  # bye bye
439 4
    return compmethod, szexe
440
441
442 4
def test_bar_files(localdir, urllist):
443
    """
444
    Test bar files after download.
445
446
    :param localdir: Directory.
447
    :type localdir: str
448
449
    :param urllist: List of URLs to check.
450
    :type urllist: list(str)
451
    """
452 4
    brokenlist = []
453 4
    print("TESTING BAR FILES...")
454 4
    for file in os.listdir(localdir):
455 4
        if file.endswith(".bar"):
456 4
            print("TESTING: {0}".format(file))
457 4
            thepath = os.path.abspath(os.path.join(localdir, file))
458 4
            brokens = barutils.bar_tester(thepath)
459 4
            if brokens is not None:
460 4
                os.remove(brokens)
461 4
                for url in urllist:
462 4
                    if brokens in url:
463 4
                        brokenlist.append(url)
464 4
    if brokenlist:
465 4
        print("SOME FILES ARE BROKEN!")
466 4
        utilities.lprint(brokenlist)
467 4
        raise SystemExit
468
    else:
469 4
        print("BAR FILES DOWNLOADED OK")
470
471
472 4
def test_signed_files(localdir):
473
    """
474
    Test signed files after extract.
475
476
    :param localdir: Directory.
477
    :type localdir: str
478
    """
479 4
    print("TESTING SIGNED FILES...")
480 4
    for file in os.listdir(localdir):
481 4
        if file.endswith(".bar"):
482 4
            print("TESTING: {0}".format(file))
483 4
            signname, signhash = barutils.retrieve_sha512(os.path.join(localdir, file))
484 4
            sha512ver = barutils.verify_sha512(os.path.join(localdir, signname.decode("utf-8")), signhash)
485 4
            if not sha512ver:
486 4
                print("{0} IS BROKEN".format((file)))
487 4
                break
488
    else:
489 4
        print("ALL FILES EXTRACTED OK")
490
491
492 4
def test_loader_files(localdir):
493
    """
494
    Test loader files after creation.
495
496
    :param localdir: Directory.
497
    :type localdir: str
498
    """
499 4
    if not utilities.is_windows():
500 4
        pass
501
    else:
502 4
        print("TESTING LOADER FILES...")
503 4
        brokens = utilities.verify_bulk_loaders(localdir)
504 4
        if brokens:
505 4
            print("BROKEN FILES:")
506 4
            utilities.lprint(brokens)
507 4
            raise SystemExit
508
        else:
509 4
            print("ALL FILES CREATED OK")
510
511
512 4
def test_single_loader(loaderfile):
513
    """
514
    Test single loader file after creation.
515
516
    :param loaderfile: File to check.
517
    :type loaderfile: str
518
    """
519 4
    if not utilities.is_windows():
520 4
        pass
521
    else:
522 4
        print("TESTING LOADER...")
523 4
        if not utilities.verify_loader_integrity(loaderfile):
524 4
            print("{0} IS BROKEN!".format(os.path.basename(loaderfile)))
525 4
            raise SystemExit
526
        else:
527 4
            print("LOADER CREATED OK")
528
529
530 4
def prod_avail(results, mailer=False, osversion=None, password=None):
531
    """
532
    Clean availability for production lookups for autolookup script.
533
534
    :param results: Result dict.
535
    :type results: dict(str: str)
536
537
    :param mailer: If we're mailing links. Default is false.
538
    :type mailer: bool
539
540
    :param osversion: OS version.
541
    :type osversion: str
542
543
    :param password: Email password.
544
    :type password: str
545
    """
546 4
    prel = results['p']
547 4
    if prel != "SR not in system" and prel is not None:
548 4
        pav = "PD"
549 4
        baseurl = utilities.create_base_url(prel)
550 4
        avail = networkutils.availability(baseurl)
551 4
        is_avail = "Available" if avail else "Unavailable"
552 4
        prod_avail_mailprep(prel, avail, osversion, mailer, password)
553
    else:
554 4
        pav = "  "
555 4
        is_avail = "Unavailable"
556 4
    return prel, pav, is_avail
557
558
559 4
def prod_avail_mailprep(prel, avail, osversion=None, mailer=False, password=None):
560
    """
561
    Do SQL/SMTP prep work after a good production lookup hit.
562
563
    :param prel: Software lookup result.
564
    :type prel: str
565
566
    :param avail: If software lookup result is available for download.
567
    :type avail: bool
568
569
    :param osversion: OS version.
570
    :type osversion: str
571
572
    :param mailer: If we're mailing links. Default is false.
573
    :type mailer: bool
574
575
    :param password: Email password.
576
    :type password: str
577
    """
578 4
    if avail and mailer:
579 4
        sqlutils.prepare_sw_db()
580 4
        if not sqlutils.check_exists(osversion, prel):
581 4
            rad = utilities.increment(osversion, 1)
582 4
            linkgen(osversion, rad, prel, temp=True)
583 4
            smtputils.prep_email(osversion, prel, password)
584
585
586 4
def linkgen(osversion, radioversion=None, softwareversion=None, altsw=None, temp=False, sdk=False):
587
    """
588
    Generate debrick/core/radio links for given OS, radio, software release.
589
590
    :param osversion: OS version, 10.x.y.zzzz.
591
    :type osversion: str
592
593
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
594
    :type radioversion: str
595
596
    :param softwareversion: Software version, 10.x.y.zzzz. Can be guessed.
597
    :type softwareversion: str
598
599
    :param altsw: Radio software release, if not the same as OS.
600
    :type altsw: str
601
602
    :param temp: If file we write to is temporary. Default is False.
603
    :type temp: bool
604
605
    :param sdk: If we specifically want SDK images. Default is False.
606
    :type sdk: bool
607
    """
608 4
    radioversion = return_radio_version(osversion, radioversion)
609 4
    softwareversion, swc = return_sw_checked(softwareversion, osversion)
610 4
    del swc
611 4
    if altsw is not None:
612 4
        altsw, aswc = return_radio_sw_checked(altsw, radioversion)
613 4
        del aswc
614 4
    baseurl = utilities.create_base_url(softwareversion)
615 4
    oses, cores, radios = textgenerator.url_gen(osversion, radioversion, softwareversion)
616 4
    if altsw is not None:
617 4
        del radios
618 4
        dbks, cors, radios = textgenerator.url_gen(osversion, radioversion, altsw)
619 4
        del dbks
620 4
        del cors
621 4
    avlty = networkutils.availability(baseurl)
622 4
    if sdk:
623 4
        oses2 = {key: val.replace("factory_sfi", "sdk") for key, val in oses.items()}
624 4
        cores2 = {key: val.replace("factory_sfi", "sdk") for key, val in cores.items()}
625 4
        oses = {key: val.replace("verizon_sfi", "sdk") for key, val in oses2.items()}
626 4
        cores = {key: val.replace("verizon_sfi", "sdk") for key, val in cores2.items()}
627 4
    prargs = (softwareversion, osversion, radioversion, oses, cores, radios, avlty, False, None, temp, altsw)
628 4
    lthr = threading.Thread(target=textgenerator.write_links, args=prargs)
629 4
    lthr.start()
630
631
632 4
def clean_swrel(swrelset):
633
    """
634
    Clean a list of software release lookups.
635
636
    :param swrelset: List of software releases.
637
    :type swrelset: set(str)
638
    """
639 4
    for i in swrelset:
640 4
        if i != "SR not in system" and i is not None:
641 4
            swrelease = i
642 4
            break
643
    else:
644 4
        swrelease = ""
645 4
    return swrelease
646
647
648 4
def autolookup_logger(record, out):
649
    """
650
    Write autolookup results to file.
651
652
    :param record: The file to log to.
653
    :type record: str
654
655
    :param out: Output block.
656
    :type out: str
657
    """
658 4
    with open(record, "a") as rec:
659 4
        rec.write("{0}\n".format(out))
660
661
662 4
def autolookup_printer(out, avail, log=False, quiet=False, record=None):
663
    """
664
    Print autolookup results, logging if specified.
665
666
    :param out: Output block.
667
    :type out: str
668
669
    :param avail: Availability. Can be "Available" or "Unavailable".
670
    :type avail: str
671
672
    :param log: If we're logging to file.
673
    :type log: bool
674
675
    :param quiet: If we only note available entries.
676
    :type quiet: bool
677
678
    :param record: If we're logging, the file to log to.
679
    :type record: str
680
    """
681 4
    if not quiet:
682 4
        avail = "Available"  # force things
683 4
    if avail.lower() == "available":
684 4
        if log:
685 4
            lthr = threading.Thread(target=autolookup_logger, args=(record, out))
686 4
            lthr.start()
687 4
        print(out)
688
689
690 4
def autolookup_output_sql(osversion, swrelease, avail, sql=False):
691
    """
692
    Add OS to SQL database.
693
694
    :param osversion: OS version.
695
    :type osversion: str
696
697
    :param swrelease: Software release.
698
    :type swrelease: str
699
700
    :param avail: "Unavailable" or "Available".
701
    :type avail: str
702
703
    :param sql: If we're adding this to our SQL database.
704
    :type sql: bool
705
    """
706 4
    if sql:
707 4
        sqlutils.prepare_sw_db()
708 4
        if not sqlutils.check_exists(osversion, swrelease):
709 4
            sqlutils.insert(osversion, swrelease, avail.lower())
710
711
712 4
def autolookup_output(osversion, swrelease, avail, avpack, sql=False):
713
    """
714
    Prepare autolookup block, and add to SQL database.
715
716
    :param osversion: OS version.
717
    :type osversion: str
718
719
    :param swrelease: Software release.
720
    :type swrelease: str
721
722
    :param avail: "Unavailable" or "Available".
723
    :type avail: str
724
725
    :param avpack: Availabilities: alpha 1 and 2, beta 1 and 2, production.
726
    :type avpack: list(str)
727
728
    :param sql: If we're adding this to our SQL database.
729
    :type sql: bool
730
    """
731 4
    othr = threading.Thread(target=autolookup_output_sql, args=(osversion, swrelease, avail, sql))
732 4
    othr.start()
733 4
    avblok = "[{0}|{1}|{2}|{3}|{4}]".format(*avpack)
734 4
    out = "OS {0} - SR {1} - {2} - {3}".format(osversion, swrelease, avblok, avail)
735 4
    return out
736
737
738 4
def clean_barlist(cleanfiles, stoppers):
739
    """
740
    Remove certain bars from barlist based on keywords.
741
742
    :param cleanfiles: List of files to clean.
743
    :type cleanfiles: list(str)
744
745
    :param stoppers: List of keywords (i.e. bar names) to exclude.
746
    :type stoppers: list(str)
747
    """
748 4
    finals = [link for link in cleanfiles if all(word not in link for word in stoppers)]
749 4
    return finals
750
751
752 4
def prep_export_cchecker(files, npc, hwid, osv, radv, swv, upgrade=False, forced=None):
753
    """
754
    Prepare carrierchecker lookup links to write to file.
755
756
    :param files: List of file URLs.
757
    :type files: list(str)
758
759
    :param npc: MCC + MNC (ex. 302220).
760
    :type npc: int
761
762
    :param hwid: Device hardware ID.
763
    :type hwid: str
764
765
    :param osv: OS version.
766
    :type osv: str
767
768
    :param radv: Radio version.
769
    :type radv: str
770
771
    :param swv: Software release.
772
    :type swv: str
773
774
    :param upgrade: Whether or not to use upgrade files. Default is false.
775
    :type upgrade: bool
776
777
    :param forced: Force a software release. None to go for latest.
778
    :type forced: str
779
    """
780 4
    if not upgrade:
781 4
        newfiles = networkutils.carrier_query(npc, hwid, True, False, forced)
782 4
        cleanfiles = newfiles[3]
783
    else:
784 4
        cleanfiles = files
785 4
    osurls, coreurls, radiourls = textgenerator.url_gen(osv, radv, swv)
786 4
    stoppers = ["8960", "8930", "8974", "m5730", "winchester"]
787 4
    finals = clean_barlist(cleanfiles, stoppers)
788 4
    return osurls, coreurls, radiourls, finals
789
790
791 4
def export_cchecker(files, npc, hwid, osv, radv, swv, upgrade=False, forced=None):
792
    """
793
    Write carrierchecker lookup links to file.
794
795
    :param files: List of file URLs.
796
    :type files: list(str)
797
798
    :param npc: MCC + MNC (ex. 302220).
799
    :type npc: int
800
801
    :param hwid: Device hardware ID.
802
    :type hwid: str
803
804
    :param osv: OS version.
805
    :type osv: str
806
807
    :param radv: Radio version.
808
    :type radv: str
809
810
    :param swv: Software release.
811
    :type swv: str
812
813
    :param upgrade: Whether or not to use upgrade files. Default is false.
814
    :type upgrade: bool
815
816
    :param forced: Force a software release. None to go for latest.
817
    :type forced: str
818
    """
819 4
    if files:
820 4
        osurls, coreurls, radiourls, finals = prep_export_cchecker(files, npc, hwid, osv, radv, swv, upgrade, forced)
821 4
        textgenerator.write_links(swv, osv, radv, osurls, coreurls, radiourls, True, True, finals)
822 4
        print("\nFINISHED!!!")
823
    else:
824 4
        print("CANNOT EXPORT, NO SOFTWARE RELEASE")
825
826
827 4
def generate_blitz_links(files, osv, radv, swv):
828
    """
829
    Generate blitz URLs (i.e. all OS and radio links).
830
    :param files: List of file URLs.
831
    :type files: list(str)
832
833
    :param osv: OS version.
834
    :type osv: str
835
836
    :param radv: Radio version.
837
    :type radv: str
838
839
    :param swv: Software release.
840
    :type swv: str
841
    """
842 4
    coreurls = [
843
        utilities.create_bar_url(swv, "winchester.factory_sfi", osv),
844
        utilities.create_bar_url(swv, "qc8960.factory_sfi", osv),
845
        utilities.create_bar_url(swv, "qc8960.factory_sfi", osv),
846
        utilities.create_bar_url(swv, "qc8960.factory_sfi_hybrid_qc8974", osv)
847
    ]
848 4
    radiourls = [
849
        utilities.create_bar_url(swv, "m5730", radv),
850
        utilities.create_bar_url(swv, "qc8960", radv),
851
        utilities.create_bar_url(swv, "qc8960.wtr", radv),
852
        utilities.create_bar_url(swv, "qc8960.wtr5", radv),
853
        utilities.create_bar_url(swv, "qc8930.wtr5", radv),
854
        utilities.create_bar_url(swv, "qc8974.wtr2", radv)
855
    ]
856 4
    return files + coreurls + radiourls
857
858
859 4
def package_blitz(bardir, swv):
860
    """
861
    Package and verify a blitz package.
862
863
    :param bardir: Path to folder containing bar files.
864
    :type bardir: str
865
866
    :param swv: Software version.
867
    :type swv: str
868
    """
869 4
    print("\nCREATING BLITZ...")
870 4
    barutils.create_blitz(bardir, swv)
871 4
    print("\nTESTING BLITZ...")
872 4
    zipver = archiveutils.zip_verify("Blitz-{0}.zip".format(swv))
873 4
    if not zipver:
874 4
        print("BLITZ FILE IS BROKEN")
875 4
        raise SystemExit
876
    else:
877 4
        shutil.rmtree(bardir)
878
879
880 4
def slim_preamble(appname):
881
    """
882
    Standard app name header.
883
884
    :param appname: Name of app.
885
    :type appname: str
886
    """
887 4
    print("~~~{0} VERSION {1}~~~".format(appname.upper(), shortversion()))
888
889
890 4
def standard_preamble(appname, osversion, softwareversion, radioversion, altsw=None):
891
    """
892
    Standard app name, OS, radio and software (plus optional radio software) print block.
893
894
    :param appname: Name of app.
895
    :type appname: str
896
897
    :param osversion: OS version, 10.x.y.zzzz. Required.
898
    :type osversion: str
899
900
    :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed.
901
    :type radioversion: str
902
903
    :param softwareversion: Software release, 10.x.y.zzzz. Can be guessed.
904
    :type softwareversion: str
905
906
    :param altsw: Radio software release, if not the same as OS.
907
    :type altsw: str
908
    """
909 4
    slim_preamble(appname)
910 4
    print("OS VERSION: {0}".format(osversion))
911 4
    print("OS SOFTWARE VERSION: {0}".format(softwareversion))
912 4
    print("RADIO VERSION: {0}".format(radioversion))
913 4
    if altsw is not None:
914 4
        print("RADIO SOFTWARE VERSION: {0}".format(altsw))
915
916
917 4
def questionnaire_device(message=None):
918
    """
919
    Get device from questionnaire.
920
    """
921 4
    message = "DEVICE (SXX100-#): " if message is None else message
922 4
    device = input(message)
923 4
    if not device:
924 4
        print("NO DEVICE SPECIFIED!")
925 4
        decorators.enter_to_exit(True)
926 4
        if not getattr(sys, 'frozen', False):
927 4
            raise SystemExit
928 4
    return device
929
930
931 4
def verify_gpg_credentials():
932
    """
933
    Read GPG key/pass from file, verify if incomplete.
934
    """
935 4
    gpgkey, gpgpass = gpgutils.gpg_config_loader()
936 4
    if gpgkey is None or gpgpass is None:
937 4
        print("NO PGP KEY/PASS FOUND")
938 4
        cont = utilities.s2b(input("CONTINUE (Y/N)?: "))
939 4
        if cont:
940 4
            gpgkey = verify_gpg_key(gpgkey)
941 4
            gpgpass, writebool = verify_gpg_pass(gpgpass)
942 4
            gpgpass2 = gpgpass if writebool else None
943 4
            gpgutils.gpg_config_writer(gpgkey, gpgpass2)
944
        else:
945 4
            gpgkey = None
946 4
    return gpgkey, gpgpass
947
948
949 4
def verify_gpg_key(gpgkey=None):
950
    """
951
    Verify GPG key.
952
953
    :param gpgkey: Key, use None to take from input.
954
    :type gpgkey: str
955
    """
956 4
    if gpgkey is None:
957 4
        gpgkey = input("PGP KEY (0x12345678): ")
958 4
        if not gpgkey.startswith("0x"):
959 4
            gpgkey = "0x{0}".format(gpgkey)   # add preceding 0x
960 4
    return gpgkey
961
962
963 4
def verify_gpg_pass(gpgpass=None):
964
    """
965
    Verify GPG passphrase.
966
967
    :param gpgpass: Passphrase, use None to take from input.
968
    :type gpgpass: str
969
    """
970 4
    if gpgpass is None:
971 4
        gpgpass = getpass.getpass(prompt="PGP PASSPHRASE: ")
972 4
        writebool = utilities.s2b(input("SAVE PASSPHRASE (Y/N)?:"))
973
    else:
974 4
        writebool = False
975 4
    return gpgpass, writebool
976
977
978 4
def bulk_hash(dirs, compressed=True, deleted=True, radios=True, hashdict=None):
979
    """
980
    Hash files in several folders based on flags.
981
982
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
983
    :type dirs: list(str)
984
985
    :param compressed: Whether to hash compressed files. True by default.
986
    :type compressed: bool
987
988
    :param deleted: Whether to delete uncompressed files. True by default.
989
    :type deleted: bool
990
991
    :param radios: Whether to hash radio autoloaders. True by default.
992
    :type radios: bool
993
994
    :param hashdict: Dictionary of hash rules, in ~\bbarchivist.ini.
995
    :type hashdict: dict({str: bool})
996
    """
997 4
    print("HASHING LOADERS...")
998 4
    utilities.cond_check(hashutils.verifier, utilities.def_args(dirs), [hashdict], radios, compressed, deleted)
999
1000
1001 4
def bulk_verify(dirs, compressed=True, deleted=True, radios=True):
1002
    """
1003
    Verify files in several folders based on flags.
1004
1005
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
1006
    :type dirs: list(str)
1007
1008
    :param compressed: Whether to hash compressed files. True by default.
1009
    :type compressed: bool
1010
1011
    :param deleted: Whether to delete uncompressed files. True by default.
1012
    :type deleted: bool
1013
1014
    :param radios: Whether to hash radio autoloaders. True by default.
1015
    :type radios: bool
1016
    """
1017 4
    gpgkey, gpgpass = verify_gpg_credentials()
1018 4
    if gpgpass is not None and gpgkey is not None:
1019 4
        print("VERIFYING LOADERS...")
1020 4
        print("KEY: {0}".format(gpgkey))
1021 4
        restargs = [gpgkey, gpgpass, True]
1022 4
        utilities.cond_check(gpgutils.gpgrunner, utilities.def_args(dirs), restargs, radios, compressed, deleted)
1023
1024
1025 4
def enn_ayy(quant):
1026
    """
1027
    Cheeky way to put a N/A placeholder for a string.
1028
1029
    :param quant: What to check if it's None.
1030
    :type quant: str
1031
    """
1032 4
    return "N/A" if quant is None else quant
1033
1034
1035 4
def generate_workfolder(folder=None):
1036
    """
1037
    Check if a folder exists, make it if it doesn't, set it to home if None.
1038
1039
    :param folder: Folder to check.
1040
    :type folder: str
1041
    """
1042 4
    if folder is None:
1043 4
        folder = os.getcwd()
1044 4
    elif folder is not None and not os.path.exists(folder):
1045 4
        os.makedirs(folder)
1046 4
    return folder
1047
1048
1049 4
def info_header(afile, osver, radio=None, software=None, device=None):
1050
    """
1051
    Write header for info file.
1052
1053
    :param afile: Open file to write to.
1054
    :type afile: File object
1055
1056
    :param osver: OS version, required for both types.
1057
    :type osver: str
1058
1059
    :param radio: Radio version, required for QNX.
1060
    :type radio: str
1061
1062
    :param software: Software release, required for QNX.
1063
    :type software: str
1064
1065
    :param device: Device type, required for Android.
1066
    :type device: str
1067
    """
1068 4
    afile.write("OS: {0}\n".format(osver))
1069 4
    if device:
1070 4
        afile.write("Device: {0}\n".format(enn_ayy(device)))
1071
    else:
1072 4
        afile.write("Radio: {0}\n".format(enn_ayy(radio)))
1073 4
        afile.write("Software: {0}\n".format(enn_ayy(software)))
1074 4
    afile.write("{0}\n".format("~"*40))
1075
1076
1077 4
def prep_info(filepath, osver, device=None):
1078
    """
1079
    Prepare file list for new-style info file.
1080
1081
    :param filepath: Path to folder to analyze.
1082
    :type filepath: str
1083
1084
    :param osver: OS version, required for both types.
1085
    :type osver: str
1086
1087
    :param device: Device type, required for Android.
1088
    :type device: str
1089
    """
1090 4
    fileext = ".zip" if device else ".7z"
1091 4
    files = os.listdir(filepath)
1092 4
    absfiles = [os.path.join(filepath, x) for x in files if x.endswith((fileext, ".exe"))]
1093 4
    fname = os.path.join(filepath, "!{0}_OSINFO!.txt".format(osver))
1094 4
    return fname, absfiles
1095
1096
1097 4
def make_info(filepath, osver, radio=None, software=None, device=None):
1098
    """
1099
    Create a new-style info (names, sizes and hashes) file.
1100
1101
    :param filepath: Path to folder to analyze.
1102
    :type filepath: str
1103
1104
    :param osver: OS version, required for both types.
1105
    :type osver: str
1106
1107
    :param radio: Radio version, required for QNX.
1108
    :type radio: str
1109
1110
    :param software: Software release, required for QNX.
1111
    :type software: str
1112
1113
    :param device: Device type, required for Android.
1114
    :type device: str
1115
    """
1116 4
    fname, absfiles = prep_info(filepath, osver, device)
1117 4
    with open(fname, "w") as afile:
1118 4
        info_header(afile, osver, radio, software, device)
1119 4
        for indx, file in enumerate(absfiles):
1120 4
            write_info(file, indx, len(absfiles), afile)
1121
1122
1123 4
def write_info(infile, index, filecount, outfile):
1124
    """
1125
    Write a new-style info (names, sizes and hashes) file.
1126
1127
    :param infile: Path to file whose name, size and hash are to be written.
1128
    :type infile: str
1129
1130
    :param index: Which file index out of the list of files we're writing.
1131
    :type index: int
1132
1133
    :param filecount: Total number of files we're to write; for excluding terminal newline.
1134
    :type filecount: int
1135
1136
    :param outfile: Open (!!!) file handle. Output file.
1137
    :type outfile: str
1138
    """
1139 4
    fsize = os.stat(infile).st_size
1140 4
    outfile.write("File: {0}\n".format(os.path.basename(infile)))
1141 4
    outfile.write("\tSize: {0} ({1})\n".format(fsize, utilities.fsizer(fsize)))
1142 4
    outfile.write("\tHashes:\n")
1143 4
    outfile.write("\t\tMD5: {0}\n".format(hashutils.hm5(infile).upper()))
1144 4
    outfile.write("\t\tSHA1: {0}\n".format(hashutils.hs1(infile).upper()))
1145 4
    outfile.write("\t\tSHA256: {0}\n".format(hashutils.hs256(infile).upper()))
1146 4
    outfile.write("\t\tSHA512: {0}\n".format(hashutils.hs512(infile).upper()))
1147 4
    if index != filecount - 1:
1148 4
        outfile.write("\n")
1149
1150
1151 4
def bulk_info(dirs, osv, compressed=True, deleted=True, radios=True, rad=None, swv=None, dev=None):
1152
    """
1153
    Generate info files in several folders based on flags.
1154
1155
    :param dirs: Folders: [OS_bars, radio_bars, OS_exes, radio_exes, OS_zips, radio_zips]
1156
    :type dirs: list(str)
1157
1158
    :param osver: OS version, required for both types.
1159
    :type osver: str
1160
1161
    :param compressed: Whether to hash compressed files. True by default.
1162
    :type compressed: bool
1163
1164
    :param deleted: Whether to delete uncompressed files. True by default.
1165
    :type deleted: bool
1166
1167
    :param radios: Whether to hash radio autoloaders. True by default.
1168
    :type radios: bool
1169
1170
    :param rad: Radio version, required for QNX.
1171
    :type rad: str
1172
1173
    :param swv: Software release, required for QNX.
1174
    :type swv: str
1175
1176
    :param dev: Device type, required for Android.
1177
    :type dev: str
1178
    """
1179 4
    print("GENERATING INFO FILES...")
1180 4
    restargs = [osv, rad, swv, dev]
1181
    utilities.cond_check(make_info, utilities.def_args(dirs), restargs, radios, compressed, deleted)
1182