|
1
|
|
|
# |
|
2
|
|
|
# Copyright (C) 2013 Red Hat, Inc. |
|
3
|
|
|
# |
|
4
|
|
|
# This copyrighted material is made available to anyone wishing to use, |
|
5
|
|
|
# modify, copy, or redistribute it subject to the terms and conditions of |
|
6
|
|
|
# the GNU General Public License v.2, or (at your option) any later version. |
|
7
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT |
|
8
|
|
|
# ANY WARRANTY expressed or implied, including the implied warranties of |
|
9
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
10
|
|
|
# Public License for more details. You should have received a copy of the |
|
11
|
|
|
# GNU General Public License along with this program; if not, write to the |
|
12
|
|
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
13
|
|
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the |
|
14
|
|
|
# source code or documentation are not subject to the GNU General Public |
|
15
|
|
|
# License and may only be used or replicated with the express permission of |
|
16
|
|
|
# Red Hat, Inc. |
|
17
|
|
|
# |
|
18
|
|
|
# Red Hat Author(s): Vratislav Podzimek <[email protected]> |
|
19
|
|
|
# |
|
20
|
|
|
|
|
21
|
|
|
"""Module with various utility functions used by the addon.""" |
|
22
|
|
|
|
|
23
|
|
|
import os |
|
24
|
|
|
import os.path |
|
25
|
|
|
import shutil |
|
26
|
|
|
import glob |
|
27
|
|
|
import hashlib |
|
28
|
|
|
|
|
29
|
|
|
|
|
30
|
|
|
def ensure_dir_exists(dirpath): |
|
31
|
|
|
""" |
|
32
|
|
|
Checks if a given directory exists and if not, it creates the directory as |
|
33
|
|
|
well as all the nonexisting directories in its path. |
|
34
|
|
|
|
|
35
|
|
|
:param dirpath: path to the directory to be checked/created |
|
36
|
|
|
:type dirpath: str |
|
37
|
|
|
|
|
38
|
|
|
""" |
|
39
|
|
|
|
|
40
|
|
|
if not dirpath: |
|
41
|
|
|
# nothing can be done for an empty string |
|
42
|
|
|
return |
|
43
|
|
|
|
|
44
|
|
|
if not os.path.isdir(dirpath): |
|
45
|
|
|
os.makedirs(dirpath) |
|
46
|
|
|
|
|
47
|
|
|
|
|
48
|
|
|
def universal_copy(src, dst): |
|
49
|
|
|
""" |
|
50
|
|
|
Function that copies the files or directories specified by the src argument |
|
51
|
|
|
to the destination given by the dst argument. It should follow the same |
|
52
|
|
|
rules as the standard 'cp' utility. |
|
53
|
|
|
|
|
54
|
|
|
:param src: source to copy -- may be a glob, file path or a directory path |
|
55
|
|
|
:type src: str |
|
56
|
|
|
:param dst: destination to copy to |
|
57
|
|
|
:type src: str |
|
58
|
|
|
|
|
59
|
|
|
""" |
|
60
|
|
|
|
|
61
|
|
|
if glob.has_magic(src): |
|
62
|
|
|
# src is a glob |
|
63
|
|
|
sources = glob.glob(src) |
|
64
|
|
|
else: |
|
65
|
|
|
# not a glob |
|
66
|
|
|
sources = [src] |
|
67
|
|
|
|
|
68
|
|
|
for item in sources: |
|
69
|
|
|
if os.path.isdir(item): |
|
70
|
|
|
if os.path.isdir(dst): |
|
71
|
|
|
item = item.rstrip("/") |
|
72
|
|
|
dirname = item.rsplit("/", 1)[-1] |
|
73
|
|
|
shutil.copytree(item, join_paths(dst, dirname)) |
|
74
|
|
|
else: |
|
75
|
|
|
shutil.copytree(item, dst) |
|
76
|
|
|
else: |
|
77
|
|
|
shutil.copy2(item, dst) |
|
78
|
|
|
|
|
79
|
|
|
|
|
80
|
|
|
def keep_type_map(func, iterable): |
|
81
|
|
|
""" |
|
82
|
|
|
Function that maps the given function to items in the given iterable |
|
83
|
|
|
keeping the type of the iterable. |
|
84
|
|
|
|
|
85
|
|
|
:param func: function to be mapped on the items in the iterable |
|
86
|
|
|
:type func: in_item -> out_item |
|
87
|
|
|
:param iterable: iterable providing the items the function should be mapped |
|
88
|
|
|
on |
|
89
|
|
|
:type iterable: iterable |
|
90
|
|
|
:return: iterable providing items produced by the function mapped on the |
|
91
|
|
|
input items |
|
92
|
|
|
:rtype: the same type as input iterable or generator if the iterable is not |
|
93
|
|
|
of any basic Python types |
|
94
|
|
|
|
|
95
|
|
|
""" |
|
96
|
|
|
|
|
97
|
|
|
if isinstance(iterable, dict): |
|
98
|
|
|
return dict((func(key), iterable[key]) for key in iterable) |
|
99
|
|
|
|
|
100
|
|
|
items_gen = (func(item) for item in iterable) |
|
|
|
|
|
|
101
|
|
|
if isinstance(iterable, list): |
|
102
|
|
|
return list(items_gen) |
|
103
|
|
|
elif isinstance(iterable, tuple): |
|
104
|
|
|
if iterable.__class__ is tuple: |
|
105
|
|
|
return tuple(items_gen) |
|
106
|
|
|
else: |
|
107
|
|
|
return iterable.__class__(*items_gen) |
|
108
|
|
|
elif isinstance(iterable, set): |
|
109
|
|
|
return set(items_gen) |
|
110
|
|
|
elif isinstance(iterable, str): |
|
111
|
|
|
return "".join(items_gen) |
|
112
|
|
|
else: |
|
113
|
|
|
return items_gen |
|
114
|
|
|
|
|
115
|
|
|
|
|
116
|
|
|
def join_paths(path1, path2): |
|
117
|
|
|
""" |
|
118
|
|
|
Joins two paths as one would expect -- i.e. just like the os.path.join |
|
119
|
|
|
function except for doing crazy things when the second argument is an |
|
120
|
|
|
absolute path. |
|
121
|
|
|
|
|
122
|
|
|
:param path1: first path |
|
123
|
|
|
:type path1: str |
|
124
|
|
|
:param path2: second path |
|
125
|
|
|
:type path2: str |
|
126
|
|
|
:return: path1 and path2 joined with the file separator |
|
127
|
|
|
:rtype: str |
|
128
|
|
|
|
|
129
|
|
|
""" |
|
130
|
|
|
|
|
131
|
|
|
# os.path.normpath doesn't squash two starting slashes |
|
132
|
|
|
path1.replace("//", "/") |
|
133
|
|
|
|
|
134
|
|
|
return os.path.normpath(path1 + os.path.sep + path2) |
|
135
|
|
|
|
|
136
|
|
|
|
|
137
|
|
|
def get_hashing_algorithm(fingerprint): |
|
138
|
|
|
""" |
|
139
|
|
|
Get hashing algorithm for the given fingerprint or None if fingerprint of |
|
140
|
|
|
unsupported length is given. |
|
141
|
|
|
|
|
142
|
|
|
:param fingerprint: hexa fingerprint to get the hashing algorithm for |
|
143
|
|
|
:type fingerprint: hexadecimal str |
|
144
|
|
|
:return: one of the hashlib.* hash objects |
|
145
|
|
|
:rtype: hashlib.HASH object |
|
146
|
|
|
|
|
147
|
|
|
""" |
|
148
|
|
|
|
|
149
|
|
|
hashes = (hashlib.md5(), hashlib.sha1(), hashlib.sha224(), |
|
150
|
|
|
hashlib.sha256(), hashlib.sha384(), hashlib.sha512()) |
|
151
|
|
|
|
|
152
|
|
|
if len(fingerprint) % 2 == 1: |
|
153
|
|
|
return None |
|
154
|
|
|
|
|
155
|
|
|
num_bytes = len(fingerprint) / 2 |
|
156
|
|
|
|
|
157
|
|
|
for hash_obj in hashes: |
|
158
|
|
|
# pylint: disable-msg=E1103 |
|
159
|
|
|
if hash_obj.digest_size == num_bytes: |
|
160
|
|
|
return hash_obj |
|
161
|
|
|
|
|
162
|
|
|
return None |
|
163
|
|
|
|
|
164
|
|
|
|
|
165
|
|
|
def get_file_fingerprint(fpath, hash_obj): |
|
166
|
|
|
""" |
|
167
|
|
|
Get fingerprint of the given file with the given hashing algorithm. |
|
168
|
|
|
|
|
169
|
|
|
:param fpath: path to the file to get fingerprint for |
|
170
|
|
|
:type fpath: str |
|
171
|
|
|
:param hash_obj: hashing algorithm to get fingerprint with |
|
172
|
|
|
:type hash_obj: hashlib.HASH |
|
173
|
|
|
:return: fingerprint of the given file with the given algorithm |
|
174
|
|
|
:rtype: hexadecimal str |
|
175
|
|
|
|
|
176
|
|
|
""" |
|
177
|
|
|
|
|
178
|
|
|
with open(fpath, "rb") as fobj: |
|
179
|
|
|
bsize = 4 * 1024 |
|
180
|
|
|
# process file as 4 KB blocks |
|
181
|
|
|
buf = fobj.read(bsize) |
|
182
|
|
|
while buf: |
|
183
|
|
|
hash_obj.update(buf) |
|
184
|
|
|
buf = fobj.read(bsize) |
|
185
|
|
|
|
|
186
|
|
|
return hash_obj.hexdigest() |
|
187
|
|
|
|