Passed
Push — master ( 816317...cc1553 )
by John
03:01
created

bbarchivist.networkutilstcl.remote_prd_info()   A

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
nop 0
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
#!/usr/bin/env python3
2 5
"""This module is used for network connections for TCL tools."""
3
4 5
import base64  # encoding
5 5
import binascii  # encoding
6 5
import hashlib  # salt
7 5
import random  # salt
8 5
import time  # salt
9 5
import zlib  # encoding
10
11 5
import requests  # downloading
12 5
from bbarchivist import networkutils  # network tools
13 5
from bbarchivist import xmlutilstcl  # xml work
14 5
from bbarchivist.bbconstants import TCLMASTERS  # lookup servers
15
16 5
__author__ = "Thurask"
17 5
__license__ = "WTFPL v2"
18 5
__copyright__ = "2018 Thurask"
19
20
21 5
def tcl_master():
22
    """
23
    Get a random master server.
24
    """
25 5
    return random.choice(TCLMASTERS)
26
27
28 5
def tcl_default_id(devid):
29
    """
30
    Get an IMEI or a serial number or something.
31
32
    :param devid: Return default if this is None.
33
    :type devid: str
34
    """
35 5
    if devid is None:
36 5
        devid = "543212345000000"
37 5
    return devid
38
39
40 5
def check_prep(curef, mode=4, fvver="AAA000", cltp=2010, cktp=2, rtd=1, chnl=2, devid=None):
41
    """
42
    Prepare variables for TCL update check.
43
44
    :param curef: PRD of the phone variant to check.
45
    :type curef: str
46
47
    :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas.
48
    :type mode: int
49
50
    :param fvver: Initial software version, must be specific if downloading OTA deltas.
51
    :type fvver: str
52
53
    :param cltp: 2010 to always show latest version, 10 to show actual updates. Default is 2010.
54
    :type cltp: int
55
56
    :param cktp: 2 if checking manually, 1 if checking automatically. Default is 2.
57
    :type cktp: int
58
59
    :param rtd: 2 if rooted, 1 if not. Default is 1.
60
    :type rtd: int
61
62
    :param chnl: 2 if checking on WiFi, 1 if checking on mobile. Default is 2.
63
    :type chnl: int
64
65
    :param devid: Serial number/IMEI. Default is fake, not that it matters.
66
    :type devid: str
67
    """
68 5
    devid = tcl_default_id(devid)
69 5
    geturl = "http://{0}/check.php".format(tcl_master())
70 5
    params = {"id": devid, "curef": curef, "fv": fvver, "mode": mode, "type": "Firmware", "cltp": cltp, "cktp": cktp, "rtd": rtd, "chnl": chnl}
71 5
    return geturl, params
72
73
74 5
@networkutils.pem_wrapper
75 5
@networkutils.try_try_again
76 5
def tcl_check(curef, session=None, mode=4, fvver="AAA000", export=False):
77
    """
78
    Check TCL server for updates.
79
80
    :param curef: PRD of the phone variant to check.
81
    :type curef: str
82
83
    :param session: Requests session object, default is created on the fly.
84
    :type session: requests.Session()
85
86
    :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas.
87
    :type mode: int
88
89
    :param fvver: Initial software version, must be specific if downloading OTA deltas.
90
    :type fvver: str
91
92
    :param export: Whether to export XML response to file. Default is False.
93
    :type export: bool
94
    """
95 5
    sess = networkutils.generic_session(session)
96 5
    geturl, params = check_prep(curef, mode, fvver)
97 5
    req = sess.get(geturl, params=params)
98 5
    if req.status_code == 200:
99 5
        req.encoding = "utf-8"
100 5
        response = req.text
101 5
        if export:
102 5
            salt = tcl_salt()
103 5
            xmlutilstcl.dump_tcl_xml(response, salt)
104
    else:
105 5
        response = None
106 5
    return response
107
108
109 5
def tcl_salt():
110
    """
111
    Generate salt value for TCL server tools.
112
    """
113 5
    millis = round(time.time() * 1000)
114 5
    tail = "{0:06d}".format(random.randint(0, 999999))
115 5
    return "{0}{1}".format(str(millis), tail)
116
117
118 5
def unpack_vdkey():
119
    """
120
    Draw the curtain back.
121
    """
122 5
    vdkey = b"eJwdjwEOwDAIAr8kKFr//7HhmqXp8AIIDrYAgg8byiUXrwRJRXja+d6iNxu0AhUooDCN9rd6rDLxmGIakUVWo3IGCTRWqCAt6X4jGEIUAxgN0eYWnp+LkpHQAg/PsO90ELsy0Npm/n2HbtPndFgGEV31R9OmT4O4nrddjc3Qt6nWscx7e+WRHq5UnOudtjw5skuV09pFhvmqnOEIs4ljPeel1wfLYUF4\n"
123 5
    vdk = zlib.decompress(binascii.a2b_base64(vdkey))
124 5
    return vdk.decode("utf-8")
125
126
127 5
def vkhash(curef, tvver, fwid, salt, mode=4, fvver="AAA000", cltp=2010, devid=None):
128
    """
129
    Generate hash from TCL update server variables.
130
131
    :param curef: PRD of the phone variant to check.
132
    :type curef: str
133
134
    :param tvver: Target software version.
135
    :type tvver: str
136
137
    :param fwid: Firmware ID for desired download file.
138
    :type fwid: str
139
140
    :param salt: Salt hash.
141
    :type salt: str
142
143
    :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas.
144
    :type mode: int
145
146
    :param fvver: Initial software version, must be specific if downloading OTA deltas.
147
    :type fvver: str
148
149
    :param cltp: 2010 to always show latest version, 10 to show actual updates. Default is 2010.
150
    :type cltp: int
151
152
    :param devid: Serial number/IMEI. Default is fake, not that it matters.
153
    :type devid: str
154
    """
155 5
    vdk = unpack_vdkey()
156 5
    devid = tcl_default_id(devid)
157 5
    query = "id={0}&salt={1}&curef={2}&fv={3}&tv={4}&type={5}&fw_id={6}&mode={7}&cltp={8}{9}".format(devid, salt, curef, fvver, tvver, "Firmware", fwid, mode, cltp, vdk)
158 5
    engine = hashlib.sha1()
159 5
    engine.update(bytes(query, "utf-8"))
160 5
    return engine.hexdigest()
161
162
163 5
def download_request_prep(curef, tvver, fwid, salt, vkh, mode=4, fvver="AAA000", cltp=2010, devid=None):
164
    """
165
    Prepare variables for download server check.
166
167
    :param curef: PRD of the phone variant to check.
168
    :type curef: str
169
170
    :param tvver: Target software version.
171
    :type tvver: str
172
173
    :param fwid: Firmware ID for desired download file.
174
    :type fwid: str
175
176
    :param salt: Salt hash.
177
    :type salt: str
178
179
    :param vkh: VDKey-based hash.
180
    :type vkh: str
181
182
    :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas.
183
    :type mode: int
184
185
    :param fvver: Initial software version, must be specific if downloading OTA deltas.
186
    :type fvver: str
187
188
    :param cltp: 2010 to always show latest version, 10 to show actual updates. Default is 2010.
189
    :type cltp: int
190
191
    :param devid: Serial number/IMEI. Default is fake, not that it matters.
192
    :type devid: str
193
    """
194 5
    devid = tcl_default_id(devid)
195 5
    posturl = "http://{0}/download_request.php".format(tcl_master())
196 5
    params = {"id": devid, "curef": curef, "fv": fvver, "mode": mode, "type": "Firmware", "tv": tvver, "fw_id": fwid, "salt": salt, "vk": vkh, "cltp": cltp}
197 5
    if mode == 4:
198 5
        params["foot"] = 1
199 5
    return posturl, params
200
201
202 5
@networkutils.pem_wrapper
203 5
@networkutils.try_try_again
204 5
def tcl_download_request(curef, tvver, fwid, salt, vkh, session=None, mode=4, fvver="AAA000", export=False):
205
    """
206
    Check TCL server for download URLs.
207
208
    :param curef: PRD of the phone variant to check.
209
    :type curef: str
210
211
    :param tvver: Target software version.
212
    :type tvver: str
213
214
    :param fwid: Firmware ID for desired download file.
215
    :type fwid: str
216
217
    :param salt: Salt hash.
218
    :type salt: str
219
220
    :param vkh: VDKey-based hash.
221
    :type vkh: str
222
223
    :param session: Requests session object, default is created on the fly.
224
    :type session: requests.Session()
225
226
    :param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas.
227
    :type mode: int
228
229
    :param fvver: Initial software version, must be specific if downloading OTA deltas.
230
    :type fvver: str
231
232
    :param export: Whether to export XML response to file. Default is False.
233
    :type export: bool
234
    """
235 5
    sess = networkutils.generic_session(session)
236 5
    posturl, params = download_request_prep(curef, tvver, fwid, salt, vkh, mode, fvver)
237 5
    req = sess.post(posturl, data=params)
238 5
    if req.status_code == 200:
239 5
        req.encoding = "utf-8"
240 5
        response = req.text
241 5
        if export:
242 5
            xmlutilstcl.dump_tcl_xml(response, salt)
243
    else:
244 5
        response = None
245 5
    return response
246
247
248 5
def encrypt_header_prep(address, encslave):
249
    """
250
    Prepare variables for encrypted header check.
251
252
    :param address: File URL minus host.
253
    :type address: str
254
255
    :param encslave: Server hosting header script.
256
    :type encslave: str
257
    """
258 5
    encs = {b"YWNjb3VudA==" : b"emhlbmdodWEuZ2Fv", b"cGFzc3dvcmQ=": b"cWFydUQ0b2s="}
259 5
    params = {base64.b64decode(key): base64.b64decode(val) for key, val in encs.items()}
260 5
    params[b"address"] = bytes(address, "utf-8")
261 5
    posturl = "http://{0}/encrypt_header.php".format(encslave)
262 5
    return posturl, params
263
264
265 5
@networkutils.pem_wrapper
266 5
def encrypt_header(address, encslave, session=None):
267
    """
268
    Check encrypted header.
269
270
    :param address: File URL minus host.
271
    :type address: str
272
273
    :param encslave: Server hosting header script.
274
    :type encslave: str
275
276
    :param session: Requests session object, default is created on the fly.
277
    :type session: requests.Session()
278
    """
279 5
    sess = networkutils.generic_session(session)
280 5
    posturl, params = encrypt_header_prep(address, encslave)
281 5
    req = sess.post(posturl, data=params)
282 5
    if req.status_code == 206:  # partial
283 5
        contentlength = int(req.headers["Content-Length"])
284 5
        sentinel = "HEADER FOUND" if contentlength == 4194320 else "NO HEADER FOUND"
285
    else:
286 5
        sentinel = None
287 5
    return sentinel
288
289
290 5
@networkutils.pem_wrapper
291
def remote_prd_info():
292
    """
293
    Get list of remote OTA versions.
294
    """
295 5
    dburl = "https://tclota.birth-online.de/json_lastupdates.php"
296 5
    req = requests.get(dburl)
297 5
    reqj = req.json()
298 5
    otadict = {val["curef"]: val["last_ota"] for val in reqj.values() if val["last_ota"] is not None}
299
    return otadict
300