Completed
Push — master ( d9d086...32d755 )
by John
07:23
created

check_radio_bulk_stop()   A

Complexity

Conditions 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

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