#include <stdio.h>
#include <windows.h>
#include <stdint.h>

struct urlhandle
{
  HKEY root;
  wchar_t *subkey;
  HKEY key;
};

struct urlhandle *
find_all_handles (struct urlhandle *result, uint32_t *result_len, uint32_t *result_size, HKEY root)
{
  DWORD index;
  LONG lresult;
  wchar_t *keybuf;
  wchar_t *classbuf;
  DWORD subkeys;
  DWORD longest_subkey;
  DWORD longest_class;
  printf ("Listing %x\n", root);
  lresult = RegQueryInfoKey (root, NULL, NULL, NULL, &subkeys, &longest_subkey, &longest_class, NULL, NULL, NULL, NULL, NULL);
  if (ERROR_SUCCESS != lresult)
  {
    printf ("Failed to query info from %x: %u\n", root, GetLastError ());
    return result;
  }
  printf ("Root %x has %u subkeys, the longest subkey name of %u wchars and class name of %u wchars\n", root, subkeys, longest_subkey, longest_class);
  keybuf = malloc (sizeof (wchar_t) * (longest_subkey + 1));
  //classbuf = malloc (sizeof (wchar_t) * (longest_class + 1));
  for (index = 0, lresult = ERROR_SUCCESS; lresult == ERROR_SUCCESS; index++)
  {
    DWORD keybuf_len = longest_subkey * sizeof (wchar_t);
    //DWORD classbuf_len = longest_class * sizeof (wchar_t);
    lresult = RegEnumKeyExW (root, index, keybuf, &keybuf_len, NULL, /*classbuf*/ NULL, /*&classbuf_len*/ NULL, NULL);
    if (ERROR_SUCCESS != lresult)
    {
      if (lresult == ERROR_MORE_DATA)
        lresult = ERROR_SUCCESS;
      if (lresult != ERROR_NO_MORE_ITEMS)
        printf ("Failed to enum key %u: %u\n", index, lresult);
      continue;
    }
    else
    {
      HKEY subkey;
      LONG lresult2;
      DWORD valtype;
      DWORD valsize, valsize2;
      BOOL success = FALSE;
      keybuf[longest_subkey] = L'\0';
      lresult2 = RegOpenKeyExW (root, keybuf, 0, KEY_QUERY_VALUE, &subkey);
      if (lresult2 != ERROR_SUCCESS)
        continue;
      lresult2 = RegQueryValueExW (subkey, L"URL Protocol", NULL, &valtype, NULL, NULL);
      if (lresult2 != ERROR_FILE_NOT_FOUND && valtype == REG_SZ)
        success = TRUE;
      if (!success)
      {
        wchar_t *valbuf;
        lresult2 = RegQueryValueExW (subkey, NULL, NULL, &valtype, NULL, &valsize);
        if (lresult2 != ERROR_SUCCESS || valtype != REG_SZ || valsize < sizeof (wchar_t))
          continue;
        valbuf = malloc (sizeof (wchar_t) + valsize);
        valsize2 = valsize;
        lresult2 = RegQueryValueExW (subkey, NULL, NULL, &valtype, valbuf, &valsize2);
        if (lresult2 == ERROR_SUCCESS)
        {
          ((char *)valbuf)[valsize] = ((char *)valbuf)[valsize - 1] = L'\0';
          if (wcsstr (valbuf, L"URI") != NULL || wcsstr (valbuf, L"URL") != NULL)
          {
            printf ("Doesn't have a proper entry, but may be URI: %x\\%S\n", root, keybuf);
          }
        }
        free (valbuf);
      }
      if (success)
      {
        struct urlhandle *url;
        printf ("Has a proper entry: %x\\%S\n", root, keybuf);
        if (*result_len == *result_size)
        {
          result = realloc (result, (*result_size == 0 ? 5 : *result_size) * 2 * sizeof (struct urlhandle));
          if (result == NULL)
          {
            lresult = ERROR_NO_MORE_ITEMS;
            continue;
          }
          *result_size = (*result_size == 0 ? 5 : *result_size) * 2;
        }
        url = &result[*result_len];
        *result_len += 1;
        url->root = root;
        url->subkey = wcsdup (keybuf);
        url->key = subkey;
      }
      else
        RegCloseKey (subkey);
    }
  }
  return result;
}

int
main (int argc, char **argv)
{
  HKEY HKCU_SOFTWARE, HKCU_SOFTWARE_CLASSES, HKLM_SOFTWARE, HKLM_SOFTWARE_CLASSES;
  LONG lresult;
  struct urlhandle *urls = NULL;
  uint32_t urls_len = 0;
  uint32_t urls_size = 0;

  lresult = RegOpenKeyExW (HKEY_CURRENT_USER, L"Software", 0, KEY_ENUMERATE_SUB_KEYS, &HKCU_SOFTWARE);
  if (lresult != ERROR_SUCCESS)
    HKCU_SOFTWARE_CLASSES = INVALID_HANDLE_VALUE;
  else
  {
    lresult = RegOpenKeyExW (HKCU_SOFTWARE, L"Classes", 0, KEY_ENUMERATE_SUB_KEYS, &HKCU_SOFTWARE_CLASSES);
    if (lresult != ERROR_SUCCESS)
      HKCU_SOFTWARE_CLASSES = INVALID_HANDLE_VALUE;
  }
  lresult = RegOpenKeyExW (HKEY_LOCAL_MACHINE, L"Software", 0, KEY_ENUMERATE_SUB_KEYS, &HKLM_SOFTWARE);
  if (lresult != ERROR_SUCCESS)
    HKLM_SOFTWARE_CLASSES = INVALID_HANDLE_VALUE;
  else
  {
    lresult = RegOpenKeyExW (HKLM_SOFTWARE, L"Classes", 0, KEY_ENUMERATE_SUB_KEYS, &HKLM_SOFTWARE_CLASSES);
    if (lresult != ERROR_SUCCESS)
      HKLM_SOFTWARE_CLASSES = INVALID_HANDLE_VALUE;
  }

  if (HKCU_SOFTWARE_CLASSES != INVALID_HANDLE_VALUE)
    urls = find_all_handles (urls, &urls_len, &urls_size, HKCU_SOFTWARE_CLASSES);
  if (HKLM_SOFTWARE_CLASSES != INVALID_HANDLE_VALUE)
    urls = find_all_handles (urls, &urls_len, &urls_size, HKLM_SOFTWARE_CLASSES);
  urls = find_all_handles (urls, &urls_len, &urls_size, HKEY_CLASSES_ROOT);
  if (HKCU_SOFTWARE_CLASSES != INVALID_HANDLE_VALUE)
    RegCloseKey (HKCU_SOFTWARE_CLASSES);
  if (HKLM_SOFTWARE_CLASSES != INVALID_HANDLE_VALUE)
    RegCloseKey (HKLM_SOFTWARE_CLASSES);
  for (lresult = 0; lresult < urls_len; lresult++)
  {
    RegCloseKey (urls[lresult].key);
    free (urls[lresult].subkey);
  }
  if (urls != NULL)
    free (urls);
  return 0;
}