diff --git a/blair-sync-rustdesk-via-ga.sh b/blair-sync-rustdesk-via-ga.sh index 5d34460..2d7771b 100644 --- a/blair-sync-rustdesk-via-ga.sh +++ b/blair-sync-rustdesk-via-ga.sh @@ -115,25 +115,48 @@ def guest_ping(vmid: int) -> bool: return proc.returncode == 0 +def decode_ga_payload(raw) -> str: + if raw is None: + return '' + if isinstance(raw, bytes): + return raw.decode('utf-8-sig', errors='replace').strip() + text = str(raw).strip() + if not text: + return '' + if text[0] in '{[#' or 'rustdesk_id' in text or re.search(r'(?m)^\s*id\s*=', text): + return text.lstrip('\ufeff') + if re.fullmatch(r'[\d\r\n\t ]+', text): + return text.strip() + blob = ''.join(text.split()) + if not blob: + return text + for candidate in (blob, blob + ('=' * ((4 - len(blob) % 4) % 4))): + try: + decoded = base64.b64decode(candidate, validate=False) + return decoded.decode('utf-8-sig', errors='replace').strip() + except (ValueError, binascii.Error): + continue + return text + + def decode_exec_payload(st: dict) -> tuple[str, str]: - out = '' - err = '' - if st.get('out-data'): - out = base64.b64decode(st['out-data']).decode('utf-8', errors='replace').strip() - if st.get('err-data'): - err = base64.b64decode(st['err-data']).decode('utf-8', errors='replace').strip() + out = decode_ga_payload(st.get('out-data')) + err = decode_ga_payload(st.get('err-data')) return out, err def parse_exec_result(st: dict) -> tuple[str | None, str | None]: - exitcode = int(st.get('exitcode', 1)) - out, err = decode_exec_payload(st) - if exitcode != 0: - return None, err or f'guest exit {exitcode}' - match = re.search(r'\d{6,12}', out) - if not match: - return None, f'no id in exec output: {out!r}' - return match.group(0), None + try: + exitcode = int(st.get('exitcode', 1)) + out, err = decode_exec_payload(st) + if exitcode != 0: + return None, err or out or f'guest exit {exitcode}' + match = re.search(r'\d{6,12}', out) + if not match: + return None, f'no id in exec output: {out!r}' + return match.group(0), None + except Exception as exc: # noqa: BLE001 + return None, f'exec decode error: {exc}' def guest_exec_text(vmid: int, command: list[str], timeout: int = 90) -> tuple[str | None, str | None]: @@ -175,24 +198,29 @@ def guest_exec_text(vmid: int, command: list[str], timeout: int = 90) -> tuple[s def decode_agent_file_content(raw) -> str | None: - if raw is None: - return None - if isinstance(raw, bytes): - return raw.decode('utf-8-sig', errors='replace') - text = str(raw).strip() - if not text: - return None - # pvesh may return decoded file body (JSON/TOML/plain text). - if text[0] in '{[#' or 'rustdesk_id' in text or re.search(r'(?m)^\s*id\s*=', text): - return text.lstrip('\ufeff') - ascii_blob = ''.join(text.split()) - if not ascii_blob: - return None - try: - decoded = base64.b64decode(ascii_blob, validate=True) - return decoded.decode('utf-8-sig', errors='replace') - except (ValueError, binascii.Error): - return text.lstrip('\ufeff') + text = decode_ga_payload(raw) + return text or None + + +def parse_id_from_content(text: str, path: str) -> str | None: + if path.lower().endswith('.json'): + try: + rid = str(json.loads(text).get('rustdesk_id', '') or '').strip() + if re.fullmatch(r'\d{6,12}', rid): + return rid + except json.JSONDecodeError: + pass + match = re.search(r'"rustdesk_id"\s*:\s*"(\d{6,12})"', text) + if match: + return match.group(1) + for pattern in ( + r'(?m)^\s*id\s*=\s*["\']?(\d{6,12})', + r'(?m)rustdesk_id\s*=\s*["\']?(\d{6,12})', + ): + match = re.search(pattern, text) + if match: + return match.group(1) + return None def guest_file_read(vmid: int, guest_path: str) -> tuple[str | None, str | None]: @@ -222,24 +250,6 @@ def guest_file_read(vmid: int, guest_path: str) -> tuple[str | None, str | None] return None, f'file-read failed for {guest_path}' -def parse_id_from_content(text: str, path: str) -> str | None: - if path.lower().endswith('.json'): - try: - rid = str(json.loads(text).get('rustdesk_id', '') or '').strip() - if re.fullmatch(r'\d{6,12}', rid): - return rid - except json.JSONDecodeError: - pass - for pattern in ( - r'(?m)^\s*id\s*=\s*["\']?(\d{6,12})', - r'(?m)rustdesk_id\s*=\s*["\']?(\d{6,12})', - ): - match = re.search(pattern, text) - if match: - return match.group(1) - return None - - def powershell_encoded(script: str) -> str: return base64.b64encode(script.strip().encode('utf-16-le')).decode('ascii') @@ -278,7 +288,7 @@ def read_rustdesk_id(vmid: int) -> tuple[str | None, str | None]: log(f'VM {vmid}: ID {rid} from guest exec') return rid, None errors.append(f'exec: {err}') - return None, errors[-1] if errors else 'unknown' + return None, '; '.join(errors[-4:]) if errors else 'unknown' def qm_description(vmid: int) -> str: