bbarchivist.scriptutils.prep_export_cchecker()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 37
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 9
nop 8
dl 0
loc 37
ccs 9
cts 9
cp 1
crap 2
rs 9.95
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

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