Completed
Push — master ( de6c4c...cc1262 )
by John
03:54
created

execute_args()   B

Complexity

Conditions 5

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
c 0
b 0
f 0
dl 0
loc 19
rs 8.5454
1
#!/usr/bin/env python3
2
"""Check Android autoloader files."""
3
4
import sys  # load arguments
5
import requests  # session
6
from bbarchivist import networkutils  # lookup
7
from bbarchivist import decorators  # Ctrl+C wrapping
8
from bbarchivist import jsonutils  # json
9
from bbarchivist import utilities  # argument filters
10
from bbarchivist import scriptutils  # default parser
11
12
__author__ = "Thurask"
13
__license__ = "WTFPL v2"
14
__copyright__ = "2015-2017 Thurask"
15
16
17
def grab_args():
18
    """
19
    Parse arguments from argparse/questionnaire.
20
21
    Invoke :func:`droidlookup.droidlookup_main` with those arguments.
22
    """
23
    if len(sys.argv) > 1:
24
        parser = scriptutils.default_parser("bb-droidlookup", "Get Android autoloaders")
25
        parser.add_argument(
26
            "branch",
27
            help="OS branch, 3 letters")
28
        parser.add_argument(
29
            "floor",
30
            help="Start of search range",
31
            default=0,
32
            nargs="?",
33
            type=int,
34
            choices=range(0, 999),
35
            metavar="floor")
36
        parser.add_argument(
37
            "-d",
38
            "--device",
39
            dest="device",
40
            help="Device to check",
41
            nargs="?",
42
            type=utilities.droidlookup_devicetype,
43
            default=None)
44
        parser.add_argument(
45
            "-c",
46
            "--ceiling",
47
            dest="ceil",
48
            help="End of search range",
49
            default=999,
50
            nargs="?",
51
            type=int,
52
            choices=range(1, 1000),
53
            metavar="ceil")
54
        parser.add_argument(
55
            "-t",
56
            "--type",
57
            help="Check SHA256/512 hashes instead",
58
            default=None,
59
            type=utilities.droidlookup_hashtype)
60
        parser.add_argument(
61
            "-s",
62
            "--single",
63
            dest="single",
64
            help="Only scan one OS build",
65
            action="store_true",
66
            default=False)
67
        parser.add_argument(
68
            "-a",
69
            "--all-devices",
70
            dest="alldevices",
71
            help="Scan all devices, not just known ones",
72
            action="store_true",
73
            default=False)
74
        args = parser.parse_args(sys.argv[1:])
75
        parser.set_defaults()
76
        execute_args(args)
77
    else:
78
        questionnaire()
79
80
81
def execute_args(args):
82
    """
83
    Get args and decide what to do with them.
84
85
    :param args: Arguments.
86
    :type args: argparse.Namespace
87
    """
88
    if args.single:
89
        args.ceil = args.floor  # range(x, x+1) == x
90
    famlist = jsonutils.load_json("droidfamilies")
91
    cleanlist = famlist[:4]  # Priv/DTEK50/DTEK60/KEYone
92
    if args.device is None:
93
        if not args.alldevices:
94
            famlist = cleanlist
95
        droidlookup_main(famlist, args.branch, args.floor, args.ceil, args.type)
96
    elif args.device not in cleanlist:
97
        print("Selected device {0} has unknown autoloader scheme!".format(args.device))
98
    else:
99
        droidlookup_main(args.device, args.branch, args.floor, args.ceil, args.type)
100
101
102
def questionnaire_single():
103
    """
104
    What to ask if only one lookup is needed.
105
    """
106
    while True:
107
        scanos = input("OS (ex. AAD250): ")
108
        branch = scanos[:3]
109
        floor = scanos[3:6]
110
        quants = [len(scanos) == 6, branch.isalpha(), floor.isdigit()]
111
        if not all(quants):
112
            print("OS MUST BE 3 LETTERS AND 3 NUMBERS, TRY AGAIN")
113
            continue
114
        else:
115
            floor = int(floor)
116
            ceil = floor
117
            break
118
    return branch, floor, ceil
119
120
121
def questionnaire_branch():
122
    """
123
    Ask about lookup branch.
124
    """
125
    while True:
126
        branch = input("BRANCH (ex. AAD): ")
127
        if len(branch) != 3 or not branch.isalpha():
128
            print("BRANCH MUST BE 3 LETTERS, TRY AGAIN")
129
            continue
130
        else:
131
            break
132
    return branch
133
134
135
def parse_floor(floor):
136
    """
137
    Check if floor value is OK.
138
139
    :param floor: Starting OS version.
140
    :type floor: int
141
    """
142
    return parse_extreme(floor, 0, 998, "INITIAL < 0, TRY AGAIN", "INITIAL > 998, TRY AGAIN")
143
144
145
def parse_ceiling(ceil, floor):
146
    """
147
    Check if ceiling value is OK.
148
149
    :param ceil: Ending OS version.
150
    :type ceil: int
151
152
    :param floor: Starting OS version.
153
    :type floor: int
154
    """
155
    return parse_extreme(ceil, floor, 999, "FINAL < INITIAL, TRY AGAIN", "FINAL > 999, TRY AGAIN")
156
157
158
def questionnaire_initial():
159
    """
160
    Ask about lookup start.
161
    """
162
    while True:
163
        try:
164
            floor = int(input("INITIAL OS (0-998): "))
165
        except ValueError:
166
            continue
167
        else:
168
            if parse_floor(floor):
169
                break
170
            else:
171
                continue
172
    return floor
173
174
175
def parse_extreme(starter, minim, maxim, mintext, maxtext):
176
    """
177
    Check if floor/ceiling value is OK.
178
179
    :param starter: Minimum/maximum OS version.
180
    :type starter: int
181
182
    :param minim: Minimum value for starter.
183
    :type minim: int
184
185
    :param maxim: Maximum value for starter.
186
    :type maxim: int
187
188
    :param mintext: What to print if starter < minim.
189
    :type mintext: str
190
191
    :param maxtext: What to print if starter > maxim.
192
    :type maxtext: str
193
    """
194
    okay = False
195
    if starter < minim:
196
        print(mintext)
197
    elif starter > maxim:
198
        print(maxtext)
199
    else:
200
        okay = True
201
    return okay
202
203
204
def questionnaire_final(floor):
205
    """
206
    Ask about lookup end.
207
208
    :param floor: Starting OS version.
209
    :type floor: int
210
    """
211
    while True:
212
        try:
213
            ceil = int(input("FINAL OS (1-999): "))
214
        except ValueError:
215
            ceil = 999
216
        else:
217
            if parse_ceiling(ceil, floor):
218
                break
219
            else:
220
                continue
221
    return ceil
222
223
224
def questionnaire():
225
    """
226
    Questions to ask if no arguments given.
227
    """
228
    single = utilities.s2b(input("SINGLE OS (Y/N)?: "))
229
    if single:
230
        branch, floor, ceil = questionnaire_single()
231
    else:
232
        branch = questionnaire_branch()
233
        floor = questionnaire_initial()
234
        ceil = questionnaire_final(floor)
235
    famlist = jsonutils.load_json("droidfamilies")  # same here
236
    droidlookup_main(famlist[:2], branch, floor, ceil)
237
    decorators.enter_to_exit(True)
238
239
240
@decorators.wrap_keyboard_except
241
def droidlookup_main(device, branch, floor=0, ceil=999, method=None):
242
    """
243
    Check the existence of Android factory images, in a range.
244
245
    :param device: Device to check.
246
    :type device: str
247
248
    :param branch: OS version, 3 letters.
249
    :type branch: str
250
251
    :param floor: Starting OS version, padded to 3 numbers. Default is 0.
252
    :type floor: int
253
254
    :param ceil: Ending OS version, padded to 3 numbers. Default is 999.
255
    :type ceil: int
256
257
    :param method: None for regular OS links, "hash256/512" for SHA256 or 512 hash.
258
    :type method: str
259
    """
260
    scriptutils.slim_preamble("DROIDLOOKUP")
261
    text = "DEVICE: ALL" if isinstance(device, list) else "DEVICE: {0}".format(device.upper())
262
    print(text)
263
    sess = requests.Session()
264
    for ver in range(floor, ceil + 1):
265
        build = "{0}{1}".format(branch.upper(), str(ver).zfill(3))
266
        print("NOW SCANNING: {0}".format(build), end="\r")
267
        results = networkutils.droid_scanner(build, device, method, sess)
268
        if results is not None:
269
            for result in results:
270
                print("{0} AVAILABLE! {1}\n".format(build, result), end="\r")
271
272
273
if __name__ == "__main__":
274
    grab_args()
275