Completed
Push — master ( f0c535...a45e98 )
by John
02:41
created

questionnaire_single()   A

Complexity

Conditions 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
c 2
b 0
f 0
dl 0
loc 17
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 questionnaire_initial():
114
    """
115
    Ask about lookup start.
116
    """
117
    while True:
118
        try:
119
            floor = int(input("INITIAL OS (0-998): "))
120
        except ValueError:
121
            continue
122
        else:
123
            mintext = "INITIAL < 0, TRY AGAIN"
124
            maxtext = "INITIAL > 998, TRY AGAIN"
125
            if parse_extreme(floor, 0, 998, mintext, maxtext):
126
                break
127
            else:
128
                continue
129
    return floor
130
131
132
def parse_extreme(starter, minim, maxim, mintext, maxtext):
133
    """
134
    Check if floor/ceiling value is OK.
135
136
    :param starter: Minimum/maximum OS version.
137
    :type starter: int
138
139
    :param minim: Minimum value for starter.
140
    :type minim: int
141
142
    :param maxim: Maximum value for starter.
143
    :type maxim: int
144
145
    :param mintext: What to print if starter < minim.
146
    :type mintext: str
147
148
    :param maxtext: What to print if starter > maxim.
149
    :type maxtext: str
150
    """
151
    okay = False
152
    if starter < minim:
153
        print(mintext)
154
    elif starter > maxim:
155
        print(maxtext)
156
    else:
157
        okay = True
158
    return okay
159
160
161
def questionnaire_final(floor):
162
    """
163
    Ask about lookup end.
164
165
    :param floor: Starting OS version.
166
    :type floor: int
167
    """
168
    while True:
169
        try:
170
            ceil = int(input("FINAL OS (1-999): "))
171
        except ValueError:
172
            ceil = 999
173
        else:
174
            mintext = "FINAL < INITIAL, TRY AGAIN"
175
            maxtext = "FINAL > 999, TRY AGAIN"
176
            if parse_extreme(ceil, floor, 999, mintext, maxtext):
177
                continue
178
            else:
179
                break
180
    return ceil
181
182
183
def questionnaire():
184
    """
185
    Questions to ask if no arguments given.
186
    """
187
    single = utilities.s2b(input("SINGLE OS (Y/N)?: "))
188
    if single:
189
        branch, floor, ceil = questionnaire_single()
190
    else:
191
        branch = questionnaire_branch()
192
        floor = questionnaire_initial()
193
        ceil = questionnaire_final(floor)
194
    famlist = jsonutils.load_json("droidfamilies")  # same here
195
    droidlookup_main(famlist[:2], branch, floor, ceil)
196
    decorators.enter_to_exit(True)
197
198
199
@decorators.wrap_keyboard_except
200
def droidlookup_main(device, branch, floor=0, ceil=999, method=None):
201
    """
202
    Check the existence of Android factory images, in a range.
203
204
    :param device: Device to check.
205
    :type device: str
206
207
    :param branch: OS version, 3 letters.
208
    :type branch: str
209
210
    :param floor: Starting OS version, padded to 3 numbers. Default is 0.
211
    :type floor: int
212
213
    :param ceil: Ending OS version, padded to 3 numbers. Default is 999.
214
    :type ceil: int
215
216
    :param method: None for regular OS links, "hash256/512" for SHA256 or 512 hash.
217
    :type method: str
218
    """
219
    scriptutils.slim_preamble("DROIDLOOKUP")
220
    text = "DEVICE: ALL" if isinstance(device, list) else "DEVICE: {0}".format(device.upper())
221
    print(text)
222
    sess = requests.Session()
223
    for ver in range(floor, ceil + 1):
224
        build = "{0}{1}".format(branch.upper(), str(ver).zfill(3))
225
        print("NOW SCANNING: {0}".format(build), end="\r")
226
        results = networkutils.droid_scanner(build, device, method, sess)
227
        if results is not None:
228
            for result in results:
229
                print("{0} AVAILABLE! {1}\n".format(build, result), end="\r")
230
231
232
if __name__ == "__main__":
233
    grab_args()
234