|
15 | 15 | import errno
|
16 | 16 | import ftplib
|
17 | 17 | import glob
|
| 18 | +import re |
| 19 | +import hashlib |
18 | 20 | # py
|
19 | 21 | import os
|
20 | 22 | import queue
|
@@ -626,7 +628,7 @@ def queue_download(stn_idx: int, date_mjd: int, src_idx: int):
|
626 | 628 |
|
627 | 629 | client = Client(on_download_result, on_client_stopped,
|
628 | 630 | src.server_id,
|
629 |
| - src.protocol, host, port, |
| 631 | + src.protocol.upper(), host, port, |
630 | 632 | src.username, src.password)
|
631 | 633 | server = Server(client)
|
632 | 634 | servers[src.server_id] = server
|
@@ -1123,29 +1125,101 @@ def refresh(self):
|
1123 | 1125 |
|
1124 | 1126 | def download(self, server_path: str, dest_path: str):
|
1125 | 1127 |
|
| 1128 | + def csn_hash_token(token_parts): |
| 1129 | + # Deobfuscated equivalent of the JavaScript a0_0x2a54 array |
| 1130 | + |
| 1131 | + # Simulate the rotation |
| 1132 | + def rotate_array(arr, count): |
| 1133 | + return arr[count:] + arr[:count] |
| 1134 | + |
| 1135 | + token_parts = rotate_array(token_parts, |
| 1136 | + 0x178) # 376 decimal, results in full cycle rotation (no effect in this case) |
| 1137 | + |
| 1138 | + # Map the indices used in the code |
| 1139 | + def a0_0x4457(index): |
| 1140 | + return token_parts[index] |
| 1141 | + |
| 1142 | + # Extracted values |
| 1143 | + challenge_prefix = a0_0x4457(0) # '5523FAE9D93BF649CCB8FBE324DE72E60B419BE9' |
| 1144 | + token_key = a0_0x4457(1) # 'challenge_token=' |
| 1145 | + |
| 1146 | + # Start with i = 0 |
| 1147 | + i = 0 |
| 1148 | + n1 = int(challenge_prefix[0], 16) # First hex digit converted to int |
| 1149 | + |
| 1150 | + # Custom function simulating the SHA1().digest() behavior |
| 1151 | + def sha1_array(s): |
| 1152 | + return list(hashlib.sha1(s.encode('utf-8')).digest()) |
| 1153 | + |
| 1154 | + # Search for correct i such that s[n1] == 0xb0 and s[n1+1] == 0x0b |
| 1155 | + while True: |
| 1156 | + combined = challenge_prefix + str(i) |
| 1157 | + sha1_bytes = sha1_array(combined) |
| 1158 | + |
| 1159 | + if sha1_bytes[n1] == 0xb0 and sha1_bytes[n1 + 1] == 0x0b: |
| 1160 | + break |
| 1161 | + |
| 1162 | + i += 1 |
| 1163 | + |
| 1164 | + return combined |
| 1165 | + |
| 1166 | + token = None |
1126 | 1167 | if 'gage' in self.base_url:
|
1127 | 1168 | result = subprocess.run(['es', 'sso', 'access', '--token'],
|
1128 | 1169 | stdout=subprocess.PIPE)
|
1129 |
| - gage_token = {'Authorization': 'Bearer ' |
| 1170 | + token = {'Authorization': 'Bearer ' |
1130 | 1171 | + result.stdout.decode('utf-8').strip()}
|
1131 | 1172 | # print(result.stdout.decode('utf-8'))
|
1132 |
| - else: |
1133 |
| - gage_token = None |
1134 |
| - |
1135 |
| - with self.session.get(self.base_url + server_path, |
1136 |
| - stream=True, |
1137 |
| - timeout=SERVER_CONNECTION_TIMEOUT, |
1138 |
| - headers=gage_token) as r: |
1139 |
| - if 200 <= r.status_code <= 299: |
1140 |
| - with open(dest_path, 'wb') as f: |
1141 |
| - shutil.copyfileobj(r.raw, f) |
1142 |
| - return None |
1143 |
| - else: |
1144 |
| - error = "%d %s" % (r.status_code, r.reason) |
1145 |
| - if 500 <= r.status_code <= 599: |
1146 |
| - raise Exception(error) |
| 1173 | + elif 'csn' in self.base_url: |
| 1174 | + token = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp," |
| 1175 | + "image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", |
| 1176 | + "Accept-Encoding": "gzip, deflate, br, zstd", |
| 1177 | + "Accept-Language": "en-US,en;q=0.9", |
| 1178 | + "Priority": "u=0, i", |
| 1179 | + "Sec-Ch-Ua": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"', |
| 1180 | + "Sec-Ch-Ua-Mobile": "20", |
| 1181 | + "Sec-Ch-Ua-Platform": '"Linux"', |
| 1182 | + "Sec-Fetch-Dest": "document", |
| 1183 | + "Sec-Fetch-Mode": "navigate", |
| 1184 | + "Sec-Fetch-Site": "none", |
| 1185 | + "Sec-Fetch-User": "?1", |
| 1186 | + "Upgrade-Insecure-Requests": "1", |
| 1187 | + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " |
| 1188 | + "Chrome/135.0.0.0 Safari/537.36", |
| 1189 | + "referer": self.base_url + server_path} |
| 1190 | + |
| 1191 | + for attempt in range(3): |
| 1192 | + with self.session.get(self.base_url + server_path, |
| 1193 | + stream=True, |
| 1194 | + timeout=SERVER_CONNECTION_TIMEOUT, |
| 1195 | + headers=token, |
| 1196 | + allow_redirects=True) as r: |
| 1197 | + if 200 <= r.status_code <= 299: |
| 1198 | + with open(dest_path, 'wb') as f: |
| 1199 | + shutil.copyfileobj(r.raw, f) |
| 1200 | + return None |
1147 | 1201 | else:
|
1148 |
| - return error |
| 1202 | + error = "%d %s" % (r.status_code, r.reason) |
| 1203 | + if 500 <= r.status_code <= 599: |
| 1204 | + if attempt < 2: |
| 1205 | + if 'csn' in self.base_url: |
| 1206 | + html_content = r.text |
| 1207 | + pattern = r"'\s*([^']+)'\s*,\s*'challenge_token='" |
| 1208 | + # Search for the challenge_token in the HTML content |
| 1209 | + match = re.search(pattern, html_content) |
| 1210 | + challenge_token = match.group(1) |
| 1211 | + token_parts = [ |
| 1212 | + challenge_token, # Presumably a SHA1 hash string |
| 1213 | + 'challenge_token=', |
| 1214 | + 'array' |
| 1215 | + ] |
| 1216 | + self.session.cookies.set("challenge_token", csn_hash_token(token_parts)) |
| 1217 | + time.sleep(3) |
| 1218 | + else: |
| 1219 | + raise Exception(error) |
| 1220 | + else: |
| 1221 | + return error |
| 1222 | + |
1149 | 1223 |
|
1150 | 1224 | def list_dir(self, server_path: str):
|
1151 | 1225 | r = self.session.get(self.base_url + server_path)
|
@@ -1453,7 +1527,9 @@ def main():
|
1453 | 1527 | tqdm.write(" ** DB MIGRATED TO NEW VERSION ** ")
|
1454 | 1528 |
|
1455 | 1529 | # Cluster Job Server
|
1456 |
| - job_server = pyJobServer.JobServer(Config, |
| 1530 | + job_server = pyJobServer.JobServer(Config, check_atx=False, |
| 1531 | + check_executables=False, |
| 1532 | + check_archive=False, |
1457 | 1533 | run_parallel=not args.noparallel)
|
1458 | 1534 |
|
1459 | 1535 | # process_file dependencies:
|
|
0 commit comments