Passed
Push — master ( 86e01c...dbaced )
by John
02:16
created

tcllib.tclrequest.TclRequestMixin.prep_request()   A

Complexity

Conditions 2

Size

Total Lines 20
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 19
nop 5
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
4
# pylint: disable=C0111,C0326,C0103
5
6
"""Tools to interface with TCL's download request API."""
7
8
import binascii
9
import hashlib
10
import random
11
import time
12
import zlib
13
from collections import OrderedDict
14
from math import floor
15
16
from defusedxml import ElementTree
17
18
19
'''
20
    private HashMap<String, String> buildDownloadUrisParams(UpdatePackageInfo updatePackageInfo) {
21
        FotaLog.m28v(TAG, "doAfterCheck");
22
        String salt = FotaUtil.salt();
23
        HashMap linkedHashMap = new LinkedHashMap();
24
        linkedHashMap.put("id", this.internalBuilder.getParam("id"));
25
        linkedHashMap.put("salt", salt);
26
        linkedHashMap.put("curef", updatePackageInfo.mCuref);
27
        linkedHashMap.put("fv", updatePackageInfo.mFv);
28
        linkedHashMap.put("tv", updatePackageInfo.mTv);
29
        linkedHashMap.put("type", "Firmware");
30
        linkedHashMap.put("fw_id", updatePackageInfo.mFirmwareId);
31
        linkedHashMap.put("mode", "2");
32
        linkedHashMap.put("vk", generateVk2((LinkedHashMap) linkedHashMap.clone()));
33
        linkedHashMap.put("cltp", "10");
34
        linkedHashMap.put("cktp", this.internalBuilder.getParam("cktp"));
35
        linkedHashMap.put("rtd", this.internalBuilder.getParam("rtd"));
36
        linkedHashMap.put("chnl", this.internalBuilder.getParam("chnl"));
37
        return linkedHashMap;
38
    }
39
'''
0 ignored issues
show
Unused Code introduced by
This string statement has no effect and could be removed.
Loading history...
40
41
VDKEY_B64Z = b"eJwdjwEOwDAIAr8kKFr//7HhmqXp8AIIDrYAgg8byiUXrwRJRXja+d6iNxu0AhUooDCN9rd6rDLxmGIakUVWo3IGCTRWqCAt6X4jGEIUAxgN0eYWnp+LkpHQAg/PsO90ELsy0Npm/n2HbtPndFgGEV31R9OmT4O4nrddjc3Qt6nWscx7e+WRHq5UnOudtjw5skuV09pFhvmqnOEIs4ljPeel1wfLYUF4\n"
42
43
44
def get_salt():
45
    """Generate cryptographic salt."""
46
    millis = floor(time.time() * 1000)
47
    tail = "{:06d}".format(random.randint(0, 999999))
48
    return "{}{}".format(str(millis), tail)
49
50
51
def get_vk2(params_dict, cltp):
52
    """Generate salted hash of API parameters."""
53
    params_dict["cltp"] = cltp
54
    query = ""
55
    for key, val in params_dict.items():
56
        if query:
57
            query += "&"
58
        query += key + "=" + str(val)
59
    vdk = zlib.decompress(binascii.a2b_base64(VDKEY_B64Z))
60
    query += vdk.decode("utf-8")
61
    engine = hashlib.sha1()
62
    engine.update(bytes(query, "utf-8"))
63
    hexhash = engine.hexdigest()
64
    return hexhash
65
66
67
class TclRequestMixin:
0 ignored issues
show
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
68
    """A mixin component for TCL's download request API."""
69
70
    def prep_request(self, curef, fvver, tvver, fw_id):
71
        """Prepare URL and device parameters for download request."""
72
        url = "https://" + self.g2master + "/download_request.php"
73
        params = OrderedDict()
74
        params["id"] = self.serid
75
        params["salt"] = get_salt()
76
        params["curef"] = curef
77
        params["fv"] = fvver
78
        params["tv"] = tvver
79
        params["type"] = self.ftype
80
        params["fw_id"] = fw_id
81
        params["mode"] = self.mode.value
82
        params["vk"] = get_vk2(params, self.cltp.value)
83
        params["cltp"] = self.cltp.value
84
        params["cktp"] = self.cktp.value
85
        params["rtd"] = self.rtd.value
86
        if self.mode == self.MODE.FULL:
87
            params["foot"] = 1
88
        params["chnl"] = self.chnl.value
89
        return url, params
90
91
    def do_request(self, curef, fvver, tvver, fw_id):
92
        """Perform download request with given parameters."""
93
        url, params = self.prep_request(curef, fvver, tvver, fw_id)
94
        # print(repr(dict(params)))
95
        req = self.sess.post(url, data=params)
96
        if req.status_code == 200:
97
            req.encoding = "utf-8"  # Force encoding as server doesn't give one
98
            self.write_dump(req.text)
99
            return req.text
100
        else:
101
            print("REQUEST: " + repr(req))
102
            print(repr(req.headers))
103
            print(repr(req.text))
104
            raise SystemExit
105
106
    @staticmethod
107
    def parse_request(xmlstr):
108
        """Parse output of ``do_request``."""
109
        root = ElementTree.fromstring(xmlstr)
110
        file = root.find("FILE_LIST").find("FILE")
111
        fileid = file.find("FILE_ID").text
112
        fileurl = file.find("DOWNLOAD_URL").text
113
        s3_fileurl_node = file.find("S3_DOWNLOAD_URL")
114
        s3_fileurl = ""
115
        if s3_fileurl_node:
116
            s3_fileurl = s3_fileurl_node.text
117
        slave_list = root.find("SLAVE_LIST").findall("SLAVE")
118
        enc_list = root.find("SLAVE_LIST").findall("ENCRYPT_SLAVE")
119
        s3_slave_list = root.find("SLAVE_LIST").findall("S3_SLAVE")
120
        slaves = [s.text for s in slave_list]
121
        encslaves = [s.text for s in enc_list]
122
        s3_slaves = [s.text for s in s3_slave_list]
123
        return fileid, fileurl, slaves, encslaves, s3_fileurl, s3_slaves
124