/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#include <iostream>

#include "FuzzingInterface.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
#include "nsIURL.h"
#include "nsIStandardURL.h"
#include "nsIURIMutator.h"
#include "nsNetUtil.h"
#include "nsNetCID.h"
#include "nsPrintfCString.h"
#include "nsString.h"
#include "mozilla/Encoding.h"
#include "mozilla/Span.h"

template <typename T>
T get_numeric(char** buf, size_t* size) {
  if (sizeof(T) > *size) {
    return 0;
  }

  T* iptr = reinterpret_cast<T*>(*buf);
  *buf += sizeof(T);
  *size -= sizeof(T);
  return *iptr;
}

nsAutoCString get_string(char** buf, size_t* size) {
  uint8_t len = get_numeric<uint8_t>(buf, size);
  if (len > *size) {
    len = static_cast<uint8_t>(*size);
  }
  nsAutoCString str(*buf, len);

  *buf += len;
  *size -= len;
  return str;
}

const char* charsets[] = {
    "Big5",         "EUC-JP",       "EUC-KR",         "gb18030",
    "gbk",          "IBM866",       "ISO-2022-JP",    "ISO-8859-10",
    "ISO-8859-13",  "ISO-8859-14",  "ISO-8859-15",    "ISO-8859-16",
    "ISO-8859-2",   "ISO-8859-3",   "ISO-8859-4",     "ISO-8859-5",
    "ISO-8859-6",   "ISO-8859-7",   "ISO-8859-8",     "ISO-8859-8-I",
    "KOI8-R",       "KOI8-U",       "macintosh",      "replacement",
    "Shift_JIS",    "UTF-16BE",     "UTF-16LE",       "UTF-8",
    "windows-1250", "windows-1251", "windows-1252",   "windows-1253",
    "windows-1254", "windows-1255", "windows-1256",   "windows-1257",
    "windows-1258", "windows-874",  "x-mac-cyrillic", "x-user-defined"};

static int FuzzingRunURIParser(const uint8_t* data, size_t size) {
  char* buf = (char*)data;

  nsCOMPtr<nsIURI> uri;
  nsAutoCString spec = get_string(&buf, &size);

  nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);

  if (NS_FAILED(rv)) {
    return 0;
  }

  uint8_t iters = get_numeric<uint8_t>(&buf, &size);
  for (int i = 0; i < iters; i++) {
    if (get_numeric<uint8_t>(&buf, &size) % 25 != 0) {
      NS_MutateURI mutator(uri);
      nsAutoCString acdata = get_string(&buf, &size);

      switch (get_numeric<uint8_t>(&buf, &size) % 12) {
        default:
          mutator.SetSpec(acdata);
          break;
        case 1:
          mutator.SetScheme(acdata);
          break;
        case 2:
          mutator.SetUserPass(acdata);
          break;
        case 3:
          mutator.SetUsername(acdata);
          break;
        case 4:
          mutator.SetPassword(acdata);
          break;
        case 5:
          mutator.SetHostPort(acdata);
          break;
        case 6:
          // Called via SetHostPort
          mutator.SetHost(acdata);
          break;
        case 7:
          // Called via multiple paths
          mutator.SetPathQueryRef(acdata);
          break;
        case 8:
          mutator.SetRef(acdata);
          break;
        case 9:
          mutator.SetFilePath(acdata);
          break;
        case 10:
          mutator.SetQuery(acdata);
          break;
        case 11: {
          const uint8_t index = get_numeric<uint8_t>(&buf, &size) %
                                (sizeof(charsets) / sizeof(char*));
          const char* charset = charsets[index];
          auto encoding = mozilla::Encoding::ForLabelNoReplacement(
              mozilla::MakeStringSpan(charset));
          mutator.SetQueryWithEncoding(acdata, encoding);
          break;
        }
      }

      nsresult rv = mutator.Finalize(uri);
      if (NS_FAILED(rv)) {
        return 0;
      }
    } else {
      nsAutoCString out;

      if (uri) {
        switch (get_numeric<uint8_t>(&buf, &size) % 26) {
          default:
            uri->GetSpec(out);
            break;
          case 1:
            uri->GetPrePath(out);
            break;
          case 2:
            uri->GetScheme(out);
            break;
          case 3:
            uri->GetUserPass(out);
            break;
          case 4:
            uri->GetUsername(out);
            break;
          case 5:
            uri->GetPassword(out);
            break;
          case 6:
            uri->GetHostPort(out);
            break;
          case 7:
            uri->GetHost(out);
            break;
          case 8: {
            int rv;
            uri->GetPort(&rv);
            break;
          }
          case 9:
            uri->GetPathQueryRef(out);
            break;
          case 10: {
            nsCOMPtr<nsIURI> other;
            bool rv;
            nsAutoCString spec = get_string(&buf, &size);
            NS_NewURI(getter_AddRefs(other), spec);
            uri->Equals(other, &rv);
            break;
          }
          case 11: {
            nsAutoCString scheme = get_string(&buf, &size);
            bool rv;
            uri->SchemeIs("https", &rv);
            break;
          }
          case 12: {
            nsAutoCString in = get_string(&buf, &size);
            uri->Resolve(in, out);
            break;
          }
          case 13:
            uri->GetAsciiSpec(out);
            break;
          case 14:
            uri->GetAsciiHostPort(out);
            break;
          case 15:
            uri->GetAsciiHost(out);
            break;
          case 16:
            uri->GetRef(out);
            break;
          case 17: {
            nsCOMPtr<nsIURI> other;
            bool rv;
            nsAutoCString spec = get_string(&buf, &size);
            NS_NewURI(getter_AddRefs(other), spec);
            uri->EqualsExceptRef(other, &rv);
            break;
          }
          case 18:
            uri->GetSpecIgnoringRef(out);
            break;
          case 19: {
            bool rv;
            uri->GetHasRef(&rv);
            break;
          }
          case 20:
            uri->GetFilePath(out);
            break;
          case 21:
            uri->GetQuery(out);
            break;
          case 22:
            uri->GetDisplayHost(out);
            break;
          case 23:
            uri->GetDisplayHostPort(out);
            break;
          case 24:
            uri->GetDisplaySpec(out);
            break;
          case 25:
            uri->GetDisplayPrePath(out);
            break;
        }
      }
    }
  }

  return 0;
}

MOZ_FUZZING_INTERFACE_RAW(nullptr, FuzzingRunURIParser, URIParser);
