Completed
Push — master ( d6f670...02bc12 )
by John
01:13
created

hs3512()   B

Complexity

Conditions 6

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
c 0
b 0
f 0
dl 0
loc 22
rs 7.7857
1
#!/usr/bin/env python3
2
"""This module is used to generate file hashes/checksums and PGP signatures."""
3
4
import zlib  # crc32/adler32
5
import hashlib  # all other hashes
6
import hmac  # escreens is a hmac, news at 11
7
import configparser  # config parsing, duh
8
import os  # path work
9
import concurrent.futures  # parallelization
10
import gnupg  # interface b/w Python, GPG
11
from bbarchivist import bbconstants  # premade stuff
12
from bbarchivist import utilities  # cores
13
14
__author__ = "Thurask"
15
__license__ = "WTFPL v2"
16
__copyright__ = "Copyright 2015-2016 Thurask"
17
18
19
def hc32(filepath, blocksize=16 * 1024 * 1024):
20
    """
21
    Return CRC32 checksum of a file.
22
23
    :param filepath: File you wish to verify.
24
    :type filepath: str
25
26
    :param blocksize: How much of file to read at once.
27
    :type blocksize: int
28
    """
29
    seed = 0
30
    with open(filepath, 'rb') as file:
31
        for chunk in iter(lambda: file.read(blocksize), b''):
32
            seed = zlib.crc32(chunk, seed)
33
    final = format(seed & 0xFFFFFFFF, "08x")
34
    return final
35
36
37
def ha32(filepath, blocksize=16 * 1024 * 1024):
38
    """
39
    Return Adler32 checksum of a file.
40
41
    :param filepath: File you wish to verify.
42
    :type filepath: str
43
44
    :param blocksize: How much of file to read at once.
45
    :type blocksize: int
46
    """
47
    asum = 1
48
    with open(filepath, 'rb') as file:
49
        while True:
50
            data = file.read(blocksize)
51
            if not data:
52
                break
53
            asum = zlib.adler32(data, asum)
54
            if asum < 0:
55
                asum += 2 ** 32
56
    final = format(asum & 0xFFFFFFFF, "08x")
57
    return final
58
59
60
def hs1(filepath, blocksize=16 * 1024 * 1024):
61
    """
62
    Return SHA-1 hash of a file.
63
64
    :param filepath: File you wish to verify.
65
    :type filepath: str
66
67
    :param blocksize: How much of file to read at once.
68
    :type blocksize: int
69
    """
70
    sha1 = hashlib.sha1()
71
    with open(filepath, 'rb') as file:
72
        while True:
73
            data = file.read(blocksize)
74
            if not data:
75
                break
76
            sha1.update(data)
77
    return sha1.hexdigest()
78
79
80
def hs224(filepath, blocksize=16 * 1024 * 1024):
81
    """
82
    Return SHA-224 hash of a file.
83
84
    :param filepath: File you wish to verify.
85
    :type filepath: str
86
87
    :param blocksize: How much of file to read at once.
88
    :type blocksize: int
89
    """
90
    sha224 = hashlib.sha224()
91
    with open(filepath, 'rb') as file:
92
        while True:
93
            data = file.read(blocksize)
94
            if not data:
95
                break
96
            sha224.update(data)
97
    return sha224.hexdigest()
98
99
100
def hs256(filepath, blocksize=16 * 1024 * 1024):
101
    """
102
    Return SHA-256 hash of a file.
103
104
    :param filepath: File you wish to verify.
105
    :type filepath: str
106
107
    :param blocksize: How much of file to read at once.
108
    :type blocksize: int
109
    """
110
    sha256 = hashlib.sha256()
111
    with open(filepath, 'rb') as file:
112
        while True:
113
            data = file.read(blocksize)
114
            if not data:
115
                break
116
            sha256.update(data)
117
    return sha256.hexdigest()
118
119
120
def hs384(filepath, blocksize=16 * 1024 * 1024):
121
    """
122
    Return SHA-384 hash of a file.
123
124
    :param filepath: File you wish to verify.
125
    :type filepath: str
126
127
    :param blocksize: How much of file to read at once.
128
    :type blocksize: int
129
    """
130
    sha384 = hashlib.sha384()
131
    with open(filepath, 'rb') as file:
132
        while True:
133
            data = file.read(blocksize)
134
            if not data:
135
                break
136
            sha384.update(data)
137
    return sha384.hexdigest()
138
139
140
def hs512(filepath, blocksize=16 * 1024 * 1024):
141
    """
142
    Return SHA-512 hash of a file.
143
144
    :param filepath: File you wish to verify.
145
    :type filepath: str
146
147
    :param blocksize: How much of file to read at once.
148
    :type blocksize: int
149
    """
150
    sha512 = hashlib.sha512()
151
    with open(filepath, 'rb') as file:
152
        while True:
153
            data = file.read(blocksize)
154
            if not data:
155
                break
156
            sha512.update(data)
157
    return sha512.hexdigest()
158
159
160
def hs3224(filepath, blocksize=16 * 1024 * 1024):
161
    """
162
    Return SHA3-224 hash of a file.
163
164
    :param filepath: File you wish to verify.
165
    :type filepath: str
166
167
    :param blocksize: How much of file to read at once.
168
    :type blocksize: int
169
    """
170
    try:
171
        sha3224 = hashlib.sha3_224()
1 ignored issue
show
Bug introduced by
The Module hashlib does not seem to have a member named sha3_224.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
172
    except AttributeError:
173
        print("REQUIRES PYTHON 3.6+")
174
    else:
175
        with open(filepath, 'rb') as file:
176
            while True:
177
                data = file.read(blocksize)
178
                if not data:
179
                    break
180
                sha3224.update(data)
181
        return sha3224.hexdigest()
182
183
184
def hs3256(filepath, blocksize=16 * 1024 * 1024):
185
    """
186
    Return SHA3-256 hash of a file.
187
188
    :param filepath: File you wish to verify.
189
    :type filepath: str
190
191
    :param blocksize: How much of file to read at once.
192
    :type blocksize: int
193
    """
194
    try:
195
        sha3256 = hashlib.sha3_256()
1 ignored issue
show
Bug introduced by
The Module hashlib does not seem to have a member named sha3_256.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
196
    except AttributeError:
197
        print("REQUIRES PYTHON 3.6+")
198
    else:
199
        with open(filepath, 'rb') as file:
200
            while True:
201
                data = file.read(blocksize)
202
                if not data:
203
                    break
204
                sha3256.update(data)
205
        return sha3256.hexdigest()
206
207
208
def hs3384(filepath, blocksize=16 * 1024 * 1024):
209
    """
210
    Return SHA3-384 hash of a file.
211
212
    :param filepath: File you wish to verify.
213
    :type filepath: str
214
215
    :param blocksize: How much of file to read at once.
216
    :type blocksize: int
217
    """
218
    try:
219
        sha3384 = hashlib.sha3_384()
1 ignored issue
show
Bug introduced by
The Module hashlib does not seem to have a member named sha3_384.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
220
    except AttributeError:
221
        print("REQUIRES PYTHON 3.6+")
222
    else:
223
        with open(filepath, 'rb') as file:
224
            while True:
225
                data = file.read(blocksize)
226
                if not data:
227
                    break
228
                sha3384.update(data)
229
        return sha3384.hexdigest()
230
231
232
def hs3512(filepath, blocksize=16 * 1024 * 1024):
233
    """
234
    Return SHA3-512 hash of a file.
235
236
    :param filepath: File you wish to verify.
237
    :type filepath: str
238
239
    :param blocksize: How much of file to read at once.
240
    :type blocksize: int
241
    """
242
    try:
243
        sha3512 = hashlib.sha3_512()
1 ignored issue
show
Bug introduced by
The Module hashlib does not seem to have a member named sha3_512.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
244
    except AttributeError:
245
        print("REQUIRES PYTHON 3.6+")
246
    else:
247
        with open(filepath, 'rb') as file:
248
            while True:
249
                data = file.read(blocksize)
250
                if not data:
251
                    break
252
                sha3512.update(data)
253
        return sha3512.hexdigest()
254
255
256
def hm5(filepath, blocksize=16 * 1024 * 1024):
257
    """
258
    Return MD5 hash of a file.
259
260
    :param filepath: File you wish to verify.
261
    :type filepath: str
262
263
    :param blocksize: How much of file to read at once.
264
    :type blocksize: int
265
    """
266
    md5 = hashlib.md5()
267
    with open(filepath, 'rb') as file:
268
        while True:
269
            data = file.read(blocksize)
270
            if not data:
271
                break
272
            md5.update(data)
273
    return md5.hexdigest()
274
275
276
def ssl_hash(filepath, method, blocksize=16 * 1024 * 1024):
277
    """
278
    Return SSL-library dependent hash of a file.
279
280
    :param filepath: File you wish to verify.
281
    :type filepath: str
282
283
    :param method: Method to use: algorithms in hashlib that are not guaranteed.
284
    :type method: str
285
286
    :param blocksize: How much of file to read at once.
287
    :type blocksize: int
288
    """
289
    try:
290
        engine = hashlib.new(method)
291
        with open(filepath, 'rb') as file:
292
            while True:
293
                data = file.read(blocksize)
294
                if not data:
295
                    break
296
                engine.update(data)
297
        return engine.hexdigest()
298
    except ValueError as exc:
299
        print(str(exc))
300
        print("{0} HASH FAILED".format(method.upper()))
301
302
303
def hm4(filepath, blocksize=16 * 1024 * 1024):
304
    """
305
    Return MD4 hash of a file; depends on system SSL library.
306
307
    :param filepath: File you wish to verify.
308
    :type filepath: str
309
310
    :param blocksize: How much of file to read at once.
311
    :type blocksize: int
312
    """
313
    return ssl_hash(filepath, "md4", blocksize)
314
315
316
def hr160(filepath, blocksize=16 * 1024 * 1024):
317
    """
318
    Return RIPEMD160 hash of a file; depends on system SSL library.
319
320
    :param filepath: File you wish to verify.
321
    :type filepath: str
322
323
    :param blocksize: How much of file to read at once.
324
    :type blocksize: int
325
    """
326
    return ssl_hash(filepath, "ripemd160", blocksize)
327
328
329
def hwp(filepath, blocksize=16 * 1024 * 1024):
330
    """
331
    Return Whirlpool hash of a file; depends on system SSL library.
332
333
    :param filepath: File you wish to verify.
334
    :type filepath: str
335
336
    :param blocksize: How much of file to read at once.
337
    :type blocksize: int
338
    """
339
    return ssl_hash(filepath, "whirlpool", blocksize)
340
341
342
def hs0(filepath, blocksize=16 * 1024 * 1024):
343
    """
344
    Return SHA-0 hash of a file; depends on system SSL library.
345
346
    :param filepath: File you wish to verify.
347
    :type filepath: str
348
349
    :param blocksize: How much of file to read at once.
350
    :type blocksize: int
351
    """
352
    return ssl_hash(filepath, "sha", blocksize)
353
354
355
def gpgfile(filepath, gpginst, key=None, pword=None):
356
    """
357
    Make ASCII-armored signature files with a given private key.
358
    Takes an instance of gnupg.GPG().
359
360
    :param filepath: File you wish to verify.
361
    :type filepath: str
362
363
    :param gpginst: Instance of Python GnuPG executable.
364
    :type gpginst: gnupg.GPG()
365
366
    :param key: Key ID. 0xABCDEF01
367
    :type key: str
368
369
    :param pword: Passphrase for key.
370
    :type pword: str
371
    """
372
    with open(filepath, "rb") as file:
373
        fname = file.name + ".asc"
374
        gpginst.sign_file(file, detach=True, keyid=key, passphrase=pword, output=fname)
375
376
377
def calculate_escreens(pin, app, uptime, duration=30):
378
    """
379
    Calculate key for the Engineering Screens based on input.
380
381
    :param pin: PIN to check. 8 character hexadecimal, lowercase.
382
    :type pin: str
383
384
    :param app: App version. 10.x.y.zzzz.
385
    :type app: str
386
387
    :param uptime: Uptime in ms.
388
    :type uptime: str
389
390
    :param duration: 1, 3, 6, 15, 30 (days).
391
    :type duration: str
392
    """
393
    #: Somehow, values for lifetimes for escreens.
394
    lifetimes = {
395
        1: "",
396
        3: "Hello my baby, hello my honey, hello my rag time gal",
397
        7: "He was a boy, and she was a girl, can I make it any more obvious?",
398
        15: "So am I, still waiting, for this world to stop hating?",
399
        30: "I love myself today, not like yesterday. "
400
    }
401
    lifetimes[30] += "I'm cool, I'm calm, I'm gonna be okay"
402
    #: Escreens magic HMAC secret.
403
    secret = 'Up the time stream without a TARDIS'
404
    duration = int(duration)
405
    if duration not in [1, 3, 6, 15, 30]:
406
        duration = 1
407
    data = pin.lower() + app + str(uptime) + lifetimes[duration]
408
    newhmac = hmac.new(secret.encode(), data.encode(), digestmod=hashlib.sha1)
409
    key = newhmac.hexdigest()[:8]
410
    return key.upper()
411
412
413
def hash_get(filename, hashfunc, workingdir, blocksize=16777216):
414
    """
415
    Generate and pretty format the hash result for a file.
416
417
    :param filename: File to hash.
418
    :type filename: str
419
420
    :param hashfunc: Hash function to use.
421
    :type hashfunc: function
422
423
    :param workingdir: Working directory.
424
    :type workingdir: str
425
426
    :param blocksize: Block size. Default is 16MB.
427
    :type blocksize: int
428
    """
429
    result = hashfunc(os.path.join(workingdir, filename), blocksize)
430
    return "{0} {1}\n".format(result.upper(), filename)
431
432
433
def base_hash(hashtype, source, workingdir, block, hashfunc, target, kwargs=None):
434
    """
435
    Generic hash function; get hash, write to file.
436
437
    :param hashtype: Hash type.
438
    :type hashtype: str
439
440
    :param source: File to be hashed; foobar.ext
441
    :type source: str
442
443
    :param workingdir: Path containing files you wish to verify.
444
    :type workingdir: str
445
446
    :param block: Blocksize, in bytes.
447
    :type block: int
448
449
    :param target: File to write to.
450
    :type target: file
451
452
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
453
    :type kwargs: dict
454
    """
455
    if kwargs[hashtype]:
456
        hash_generic = [hashtype.upper()]
457
        hash_generic.append(hash_get(source, hashfunc, workingdir, block))
458
        target.write("\n".join(hash_generic))
459
460
461
def hash_writer(source, dest, workingdir, kwargs=None):
462
    """
463
    Write per-file hashes.
464
465
    :param source: File to be hashed; foobar.ext
466
    :type source: str
467
468
    :param dest: Destination file; foobar.ext.cksum
469
    :type dest: str
470
471
    :param workingdir: Path containing files you wish to verify.
472
    :type workingdir: str
473
474
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
475
    :type kwargs: dict
476
    """
477
    block = int(kwargs['blocksize'])
478
    with open(dest, 'w') as target:
479
        base_hash("adler32", source, workingdir, block, ha32, target, kwargs)
480
        base_hash("crc32", source, workingdir, block, hc32, target, kwargs)
481
        base_hash("md4", source, workingdir, block, hm4, target, kwargs)
482
        base_hash("md5", source, workingdir, block, hm5, target, kwargs)
483
        base_hash("sha0", source, workingdir, block, hs0, target, kwargs)
484
        base_hash("sha1", source, workingdir, block, hs1, target, kwargs)
485
        base_hash("sha224", source, workingdir, block, hs224, target, kwargs)
486
        base_hash("sha256", source, workingdir, block, hs256, target, kwargs)
487
        base_hash("sha384", source, workingdir, block, hs384, target, kwargs)
488 View Code Duplication
        base_hash("sha512", source, workingdir, block, hs512, target, kwargs)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
489
        base_hash("ripemd160", source, workingdir, block, hr160, target, kwargs)
490
        base_hash("whirlpool", source, workingdir, block, hwp, target, kwargs)
491
        base_hash("sha3224", source, workingdir, block, hs3224, target, kwargs)
492
        base_hash("sha3256", source, workingdir, block, hs3256, target, kwargs)
493
        base_hash("sha3384", source, workingdir, block, hs3384, target, kwargs)
494
        base_hash("sha3512", source, workingdir, block, hs3512, target, kwargs)
495
496
497
def filefilter(file, workingdir, extras=()):
498
    """
499
    Check if file in folder is a folder, or if it's got a forbidden extension.
500
501
    :param file: File to be hashed.
502
    :type file: str
503
504
    :param workingdir: Path containing files you wish to verify.
505
    :type workingdir: str
506
507
    :param extras: Tuple of extra extensions.
508
    :type extras: tuple
509
    """
510
    return not (os.path.isdir(os.path.join(workingdir, file)) or file.endswith(bbconstants.SUPPS + extras))
511
512
513
def verifier(workingdir, kwargs=None, selective=False):
514
    """
515
    For all files in a directory, perform various hash/checksum functions.
516
    Take dict to define hashes, write output to a/individual .cksum file(s).
517
518
    :param workingdir: Path containing files you wish to verify.
519
    :type workingdir: str
520
521
    :param kwargs: Values. Refer to `:func:verifier_config_loader`.
522
    :type kwargs: dict
523
    """
524
    if kwargs is None:
525
        kwargs = verifier_config_loader()
526
    extras = (".txt",) if selective else ()
527
    files = [file for file in os.listdir(workingdir) if filefilter(file, workingdir, extras)]
528
    with concurrent.futures.ThreadPoolExecutor(max_workers=utilities.workers(files)) as xec:
529
        for file in files:
530
            print("HASHING:", str(file))
531
            basename = file + ".cksum"
532
            targetname = os.path.join(workingdir, basename)
533
            try:
534
                xec.submit(hash_writer, file, targetname, workingdir, kwargs)
535
            except Exception as exc:
536
                print("SOMETHING WENT WRONG")
537
                print(str(exc))
538
                raise SystemExit
539
540
541
def gpgrunner(workingdir, keyid=None, pword=None, selective=False):
542
    """
543
    Create ASCII-armored PGP signatures for all files in a given directory, in parallel.
544
545
    :param workingdir: Path containing files you wish to verify.
546
    :type workingdir: str
547
548
    :param keyid: Key to use. 8-character hexadecimal, with or without 0x.
549
    :type keyid: str
550
551
    :param pword: Passphrase for given key.
552
    :type pword: str
553
554
    :param selective: Filtering filenames/extensions. Default is false.
555
    :type selective: bool
556
    """
557
    try:
558
        gpg = gnupg.GPG()
559
    except ValueError:
560
        print("COULD NOT FIND GnuPG!")
561
        raise SystemExit
562
    else:
563
        keyid = "0x" + keyid.upper() if not keyid.startswith("0x") else keyid.upper()
564
        dirlist = os.listdir(workingdir)
565
        files = [file for file in dirlist if not os.path.isdir(file)]
566
        with concurrent.futures.ThreadPoolExecutor(max_workers=utilities.workers(files)) as xec:
567
            for file in files:
568
                gpgwriter(gpg, xec, file, workingdir, selective, keyid, pword)
569
570
571
def gpgwriter(gpg, xec, file, workingdir, selective=False, keyid=None, pword=None):
572
    """
573
    :param gpg: Instance of Python GnuPG executable.
574
    :type gpg: gnupg.GPG()
575
576
    :param xec: ThreadPoolExecutor instance.
577
    :type xec: concurrent.futures.ThreadPoolExecutor
578
579
    :param file: File inside workingdir that is being verified.
580
    :type file: str
581
582
    :param workingdir: Path containing files you wish to verify.
583
    :type workingdir: str
584
585
    :param selective: Filtering filenames/extensions. Default is false.
586
    :type selective: bool
587
588
    :param keyid: Key to use. 8-character hexadecimal, with or without 0x.
589
    :type keyid: str
590
591
    :param pword: Passphrase for given key.
592
    :type pword: str
593
    """
594
    sup = bbconstants.SUPPS + (".txt",) if selective else bbconstants.SUPPS
595
    if not file.endswith(sup):
596
        aps = bbconstants.ARCSPLUS
597
        pfx = bbconstants.PREFIXES
598
        if (utilities.prepends(file, pfx, aps)) if selective else True:
599
            print("VERIFYING:", str(file))
600
            thepath = os.path.join(workingdir, file)
601
            try:
602
                xec.submit(gpgfile, thepath, gpg, keyid, pword)
603
            except Exception as exc:
604
                print("SOMETHING WENT WRONG")
605
                print(str(exc))
606
                raise SystemExit
607
608
609
def gpg_config_loader(homepath=None):
610
    """
611
    Read a ConfigParser file to get PGP key, password (optional)
612
613
    :param homepath: Folder containing ini file. Default is user directory.
614
    :type homepath: str
615
    """
616
    config = configparser.ConfigParser()
617
    if homepath is None:
618
        homepath = os.path.expanduser("~")
619
    conffile = os.path.join(homepath, "bbarchivist.ini")
620
    if not os.path.exists(conffile):
621
        open(conffile, 'w').close()
622
    config.read(conffile)
623
    if not config.has_section('gpgrunner'):
624
        config['gpgrunner'] = {}
625
    gpgkey = config.get('gpgrunner', 'key', fallback=None)
626
    gpgpass = config.get('gpgrunner', 'pass', fallback=None)
627
    return gpgkey, gpgpass
628
629
630
def gpg_config_writer(key=None, password=None, homepath=None):
631
    """
632
    Write a ConfigParser file to store PGP key, password (optional)
633
634
    :param key: Key ID, leave as None to not write.
635
    :type key: str
636
637
    :param password: Key password, leave as None to not write.
638
    :type password: str
639
640
    :param homepath: Folder containing ini file. Default is user directory.
641
    :type homepath: str
642
    """
643
    config = configparser.ConfigParser()
644
    if homepath is None:
645
        homepath = os.path.expanduser("~")
646
    conffile = os.path.join(homepath, "bbarchivist.ini")
647
    if not os.path.exists(conffile):
648
        open(conffile, 'w').close()
649
    config.read(conffile)
650
    if not config.has_section('gpgrunner'):
651
        config['gpgrunner'] = {}
652
    if key is not None:
653
        config['gpgrunner']['key'] = key
654
    if password is not None:
655
        config['gpgrunner']['pass'] = password
656
    with open(conffile, "w") as configfile:
657
        config.write(configfile)
658
659
660
def verifier_config_loader(homepath=None):
661
    """
662
    Read a ConfigParser file to get hash preferences.
663
664
    :param homepath: Folder containing ini file. Default is user directory.
665
    :type homepath: str
666
    """
667
    results = {}
668
    config = configparser.ConfigParser()
669
    if homepath is None:
670
        homepath = os.path.expanduser("~")
671
    conffile = os.path.join(homepath, "bbarchivist.ini")
672
    if not os.path.exists(conffile):
673
        open(conffile, 'w').close()
674
    config.read(conffile)
675
    if not config.has_section('hashmodes'):
676
        config['hashmodes'] = {}
677
    ini = config['hashmodes']
678
    results['crc32'] = bool(ini.getboolean('crc32', fallback=False))
679
    results['adler32'] = bool(ini.getboolean('adler32', fallback=False))
680
    results['sha0'] = bool(ini.getboolean('sha0', fallback=False))
681
    results['sha1'] = bool(ini.getboolean('sha1', fallback=True))
682
    results['sha224'] = bool(ini.getboolean('sha224', fallback=False))
683
    results['sha256'] = bool(ini.getboolean('sha256', fallback=True))
684
    results['sha384'] = bool(ini.getboolean('sha384', fallback=False))
685
    results['sha512'] = bool(ini.getboolean('sha512', fallback=False))
686
    results['md5'] = bool(ini.getboolean('md5', fallback=True))
687
    results['md4'] = bool(ini.getboolean('md4', fallback=False))
688
    results['ripemd160'] = bool(ini.getboolean('ripemd160', fallback=False))
689
    results['whirlpool'] = bool(ini.getboolean('whirlpool', fallback=False))
690
    results['blocksize'] = int(ini.getint('blocksize', fallback=16777216))
691
    results['sha3224'] = bool(ini.getboolean('sha3224', fallback=False))
692
    results['sha3256'] = bool(ini.getboolean('sha3256', fallback=False))
693
    results['sha3384'] = bool(ini.getboolean('sha3384', fallback=False))
694
    results['sha3512'] = bool(ini.getboolean('sha3512', fallback=False))
695
    return results
696
697
698
def verifier_config_writer(resultdict=None, homepath=None):
699
    """
700
    Write a ConfigParser file to store hash preferences.
701
702
    :param resultdict: Dictionary of results: {method, bool}
703
    :type resultdict: dict({str, bool})
704
705
    :param homepath: Folder containing ini file. Default is user directory.
706
    :type homepath: str
707
    """
708
    if resultdict is None:
709
        resultdict = verifier_config_loader()
710
    config = configparser.ConfigParser()
711
    if homepath is None:
712
        homepath = os.path.expanduser("~")
713
    conffile = os.path.join(homepath, "bbarchivist.ini")
714
    if not os.path.exists(conffile):
715
        open(conffile, 'w').close()
716
    config.read(conffile)
717
    if not config.has_section('hashmodes'):
718
        config['hashmodes'] = {}
719
    for method, flag in resultdict.items():
720
        config.set('hashmodes', method, str(flag).lower())
721
    with open(conffile, "w") as configfile:
722
        config.write(configfile)
723