/*
 * Ibis - IRCv3 Library
 * Copyright (C) 2022-2025 Ibis Developers
 *
 * Ibis is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

#include <errno.h>

#include "ibisdcc.h"

#include "ibisstring.h"

/******************************************************************************
 * Public API
 *****************************************************************************/
GInetAddress *
ibis_dcc_host_parse(const char *host) {
	GInetAddress *address = NULL;
	gulong parsed_value = 0;
	guint32 network_value = 0;
	char *endptr = NULL;

	g_return_val_if_fail(!ibis_str_is_empty(host), NULL);

	/* Attempt to create the address directly from the string. */
	address = g_inet_address_new_from_string(host);
	if(G_IS_INET_ADDRESS(address)) {
		return address;
	}

	/* That didn't work, so let's see if this is an integer that we can turn
	 * into an IPv4 address.
	 */
	parsed_value = strtoul(host, &endptr, 10);

	/* ULONG_MAX is returned on an overflow. */
	if(parsed_value == ULONG_MAX) {
		if(errno == ERANGE || errno == EINVAL) {
			return NULL;
		}
	}

	/* If parsing failed, endptr will point to the string value. */
	if(endptr == host) {
		return NULL;
	}

	/* Finally make sure we're within our expected range. */
	if(parsed_value > G_MAXUINT32) {
		return NULL;
	}

	network_value = g_htonl(parsed_value & 0xffffffff);
	address = g_inet_address_new_from_bytes((const guint8 *)&network_value,
	                                        G_SOCKET_FAMILY_IPV4);
	if(G_IS_INET_ADDRESS(address)) {
		return address;
	}

	return NULL;
}

char *
ibis_dcc_host_to_string(GInetAddress *address) {
	GSocketFamily family = G_SOCKET_FAMILY_INVALID;

	g_return_val_if_fail(G_IS_INET_ADDRESS(address), NULL);

	family = g_inet_address_get_family(address);
	switch(family) {
	case G_SOCKET_FAMILY_IPV4: {
		const guint8 *bytes = NULL;

		bytes = g_inet_address_to_bytes(address);
		if(g_inet_address_get_native_size(address) == 4) {
			guint32 *network = (guint32 *)bytes;
			return g_strdup_printf("%u", g_ntohl(*network));
		}

		break;
	}
	case G_SOCKET_FAMILY_IPV6:
		return g_inet_address_to_string(address);
		break;
	case G_SOCKET_FAMILY_INVALID:
	case G_SOCKET_FAMILY_UNIX:
	default:
		break;
	}

	return NULL;
}
