Completed
Push — master ( a45e98...12684e )
by John
02:37
created

parse_floor()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
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__ = "Copyright 2015-2016 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
        args = parser.parse_args(sys.argv[1:])
68
        parser.set_defaults()
69
        if args.single:
70
            args.ceil = args.floor  # range(x, x+1) == x
71
        if args.device is None:
72
            famlist = jsonutils.load_json("droidfamilies")
73
            droidlookup_main(famlist, args.branch, args.floor, args.ceil, args.type)
74
        else:
75
            droidlookup_main(args.device, args.branch, args.floor, args.ceil, args.type)
76
    else:
77
        questionnaire()
78
79
80
def questionnaire_single():
81
    """
82
    What to ask if only one lookup is needed.
83
    """
84
    while True:
85
        scanos = input("OS (ex. AAD250): ")
86
        branch = scanos[:3]
87
        floor = scanos[3:6]
88
        quants = [len(scanos) == 6, branch.isalpha(), floor.isdigit()]
89
        if not all(quants):
90
            print("OS MUST BE 3 LETTERS AND 3 NUMBERS, TRY AGAIN")
91
            continue
92
        else:
93
            floor = int(floor)
94
            ceil = floor
95
            break
96
    return branch, floor, ceil
97
98
99
def questionnaire_branch():
100
    """
101
    Ask about lookup branch.
102
    """
103
    while True:
104
        branch = input("BRANCH (ex. AAD): ")
105
        if len(branch) != 3 or not branch.isalpha():
106
            print("BRANCH MUST BE 3 LETTERS, TRY AGAIN")
107
            continue
108
        else:
109
            break
110
    return branch
111
112
113
def parse_floor(floor):
114
    """
115
    Check if floor value is OK.
116
117
    :param floor: Starting OS version.
118
    :type floor: int
119
    """
120
    return parse_extreme(floor, 0, 998, "INITIAL < 0, TRY AGAIN", "INITIAL > 998, TRY AGAIN")
121
122
123
def parse_ceiling(ceil, floor):
124
    """
125
    Check if ceiling value is OK.
126
127
    :param ceil: Ending OS version.
128
    :type ceil: int
129
130
    :param floor: Starting OS version.
131
    :type floor: int
132
    """
133
    return parse_extreme(ceil, floor, 999, "FINAL < INITIAL, TRY AGAIN", "FINAL > 999, TRY AGAIN")
134
135
136
def questionnaire_initial():
137
    """
138
    Ask about lookup start.
139
    """
140
    while True:
141
        try:
142
            floor = int(input("INITIAL OS (0-998): "))
143
        except ValueError:
144
            continue
145
        else:
146
            if parse_floor(floor):
147
                break
148
            else:
149
                continue
150
    return floor
151
152
153
def parse_extreme(starter, minim, maxim, mintext, maxtext):
154
    """
155
    Check if floor/ceiling value is OK.
156
157
    :param starter: Minimum/maximum OS version.
158
    :type starter: int
159
160
    :param minim: Minimum value for starter.
161
    :type minim: int
162
163
    :param maxim: Maximum value for starter.
164
    :type maxim: int
165
166
    :param mintext: What to print if starter < minim.
167
    :type mintext: str
168
169
    :param maxtext: What to print if starter > maxim.
170
    :type maxtext: str
171
    """
172
    okay = False
173
    if starter < minim:
174
        print(mintext)
175
    elif starter > maxim:
176
        print(maxtext)
177
    else:
178
        okay = True
179
    return okay
180
181
182
def questionnaire_final(floor):
183
    """
184
    Ask about lookup end.
185
186
    :param floor: Starting OS version.
187
    :type floor: int
188
    """
189
    while True:
190
        try:
191
            ceil = int(input("FINAL OS (1-999): "))
192
        except ValueError:
193
            ceil = 999
194
        else:
195
            if parse_ceiling(ceil, floor):
196
                break
197
            else:
198
                continue
199
    return ceil
200
201
202
def questionnaire():
203
    """
204
    Questions to ask if no arguments given.
205
    """
206
    single = utilities.s2b(input("SINGLE OS (Y/N)?: "))
207
    if single:
208
        branch, floor, ceil = questionnaire_single()
209
    else:
210
        branch = questionnaire_branch()
211
        floor = questionnaire_initial()
212
        ceil = questionnaire_final(floor)
213
    famlist = jsonutils.load_json("droidfamilies")  # same here
214
    droidlookup_main(famlist[:2], branch, floor, ceil)
215
    decorators.enter_to_exit(True)
216
217
218
@decorators.wrap_keyboard_except
219
def droidlookup_main(device, branch, floor=0, ceil=999, method=None):
220
    """
221
    Check the existence of Android factory images, in a range.
222
223
    :param device: Device to check.
224
    :type device: str
225
226
    :param branch: OS version, 3 letters.
227
    :type branch: str
228
229
    :param floor: Starting OS version, padded to 3 numbers. Default is 0.
230
    :type floor: int
231
232
    :param ceil: Ending OS version, padded to 3 numbers. Default is 999.
233
    :type ceil: int
234
235
    :param method: None for regular OS links, "hash256/512" for SHA256 or 512 hash.
236
    :type method: str
237
    """
238
    scriptutils.slim_preamble("DROIDLOOKUP")
239
    text = "DEVICE: ALL" if isinstance(device, list) else "DEVICE: {0}".format(device.upper())
240
    print(text)
241
    sess = requests.Session()
242
    for ver in range(floor, ceil + 1):
243
        build = "{0}{1}".format(branch.upper(), str(ver).zfill(3))
244
        print("NOW SCANNING: {0}".format(build), end="\r")
245
        results = networkutils.droid_scanner(build, device, method, sess)
246
        if results is not None:
247
            for result in results:
248
                print("{0} AVAILABLE! {1}\n".format(build, result), end="\r")
249
250
251
if __name__ == "__main__":
252
    grab_args()
253