bbarchivist.scriptutils.generate_workfolder()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nop 1
dl 0
loc 11
ccs 5
cts 5
cp 1
crap 3
rs 10
c 0
b 0
f 0
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