#!/usr/bin/env python3
"""
Spaceweb DNS API — управление DNS записями через JSON-RPC.
Панель: https://vps.sweb.ru
Домены: niikn.com, dttb.ru, itilegent.ru

Использование:
  python3 spaceweb-dns-api.py info niikn.com
  python3 spaceweb-dns-api.py add-mx niikn.com mail.niikn.com 10
  python3 spaceweb-dns-api.py add-txt niikn.com @ "v=spf1 mx ~all"
  python3 spaceweb-dns-api.py add-txt niikn.com _dmarc "v=DMARC1; p=none"
  python3 spaceweb-dns-api.py del niikn.com TXT 1
  python3 spaceweb-dns-api.py zone niikn.com
"""

import urllib.request
import urllib.parse
import json
import http.cookiejar
import sys

LOGIN = "it5870yand"
PASSWORD = "1qaz!QAZ"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"


def create_session():
    """Логин в Spaceweb, возвращает opener с куками."""
    cj = http.cookiejar.CookieJar()
    opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))

    # GET login page
    req = urllib.request.Request("https://mcp.sweb.ru/")
    req.add_header("User-Agent", UA)
    opener.open(req)

    # POST auth
    login_data = urllib.parse.urlencode({
        "login": LOGIN, "password": PASSWORD,
        "new_panel": "1", "to": "//mcp.sweb.ru/main/index/", "savepref": ""
    }).encode()
    req = urllib.request.Request("https://mcp.sweb.ru/main/auth_submit/",
                                 data=login_data, method="POST")
    req.add_header("Content-Type", "application/x-www-form-urlencoded")
    req.add_header("User-Agent", UA)
    req.add_header("Referer", "https://mcp.sweb.ru/main/auth/")
    resp = opener.open(req)
    if "vps.sweb.ru" not in resp.url and "mcp.sweb.ru/main/index" not in resp.url:
        raise RuntimeError(f"Login failed, redirected to: {resp.url}")
    return opener


def api_call(opener, method, params):
    """Вызов JSON-RPC API Spaceweb."""
    payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "user": LOGIN,
        "method": method,
        "params": params
    }
    body = urllib.parse.quote(json.dumps(payload, separators=(",", ":")), safe="").encode()
    req = urllib.request.Request("https://api.sweb.ru/domains/dns",
                                 data=body, method="POST")
    req.add_header("Content-Type", "application/x-www-form-urlencoded")
    req.add_header("User-Agent", UA)
    req.add_header("Origin", "https://vps.sweb.ru")
    req.add_header("Referer", "https://vps.sweb.ru/")
    resp = opener.open(req)
    result = json.loads(resp.read().decode())
    if "error" in result:
        raise RuntimeError(f"API error: {result['error']['message']}")
    return result.get("result")


def cmd_info(opener, domain):
    """Показать все DNS записи."""
    records = api_call(opener, "info", {"domain": domain})
    print(f"DNS записи для {domain}:")
    for r in records:
        t = r.get("type", "?")
        n = r.get("name", "@") or "@"
        v = r.get("value", "")
        idx = r.get("index", "?")
        extra = f" (pri: {r.get('priority')})" if r.get("priority") else ""
        print(f"  [{idx:>2}] {t:5s} {n:30s} → {v[:100]}{extra}")


def cmd_zone(opener, domain):
    """Показать zone file."""
    result = api_call(opener, "getFile", {"domain": domain})
    print(result["content"])


def cmd_add_mx(opener, domain, value, priority="10"):
    """Добавить MX запись."""
    if not value.endswith("."):
        value += "."
    result = api_call(opener, "editMx", {
        "domain": domain, "subDomain": "", "action": "add",
        "priority": str(priority), "value": value
    })
    print(f"MX добавлен: {domain} → {value} (pri {priority}): {result}")


def cmd_add_txt(opener, domain, subdomain, value):
    """Добавить TXT запись."""
    result = api_call(opener, "editTxt", {
        "domain": domain, "action": "add",
        "subDomain": subdomain, "value": value
    })
    print(f"TXT добавлен: {subdomain}.{domain} → {value[:60]}...: {result}")


def cmd_add_a(opener, domain, name, ip):
    """Добавить A запись (субдомен)."""
    result = api_call(opener, "editMain", {
        "domain": domain, "action": "add",
        "name": name, "type": "A", "value": ip, "prefix": ""
    })
    print(f"A добавлен: {name}.{domain} → {ip}: {result}")


def cmd_del(opener, domain, record_type, index):
    """Удалить запись по типу и индексу."""
    method_map = {
        "A": "editMain", "AAAA": "editMain", "CNAME": "editMain",
        "MX": "editMx", "TXT": "editTxt", "NS": "editNs",
        "SRV": "editSrv", "CAA": "editCaa"
    }
    method = method_map.get(record_type.upper(), "editMain")
    result = api_call(opener, method, {
        "domain": domain, "action": "del",
        "index": int(index), "type": record_type.upper()
    })
    print(f"Удалено: {record_type} index={index}: {result}")


def main():
    if len(sys.argv) < 3:
        print(__doc__)
        sys.exit(1)

    cmd = sys.argv[1]
    domain = sys.argv[2]
    opener = create_session()

    if cmd == "info":
        cmd_info(opener, domain)
    elif cmd == "zone":
        cmd_zone(opener, domain)
    elif cmd == "add-mx" and len(sys.argv) >= 4:
        pri = sys.argv[4] if len(sys.argv) > 4 else "10"
        cmd_add_mx(opener, domain, sys.argv[3], pri)
    elif cmd == "add-txt" and len(sys.argv) >= 5:
        cmd_add_txt(opener, domain, sys.argv[3], sys.argv[4])
    elif cmd == "add-a" and len(sys.argv) >= 5:
        cmd_add_a(opener, domain, sys.argv[3], sys.argv[4])
    elif cmd == "del" and len(sys.argv) >= 5:
        cmd_del(opener, domain, sys.argv[3], sys.argv[4])
    else:
        print(__doc__)
        sys.exit(1)


if __name__ == "__main__":
    main()
