/* gcc `pkg-config --cflags --libs glib-2.0 gio-2.0` srv.c -g -o srv && ./srv */

#include <glib.h>
#include <gio/gio.h>
#include <string.h>
#include <stdio.h>

typedef struct _ESrvRecord {
	gchar *service;
	gchar *hostname;
	guint16 port;
	guint16 priority;
	guint16 weight;
} ESrvRecord;

static ESrvRecord *
e_srv_record_new (const gchar *service,
		  const gchar *hostname,
		  guint16 port,
		  guint16 priority,
		  guint16 weight)
{
	ESrvRecord *srv;

	g_return_val_if_fail (service && *service, NULL);
	g_return_val_if_fail (hostname && *hostname, NULL);

	srv = g_new0 (ESrvRecord, 1);
	srv->service = g_strdup (service);
	srv->hostname = g_strdup (hostname);
	srv->port = port;
	srv->priority = priority;
	srv->weight = weight;

	return srv;
}

static void
e_srv_record_free (gpointer ptr)
{
	ESrvRecord *srv = ptr;

	if (ptr) {
		g_free (srv->service);
		g_free (srv->hostname);
		g_free (srv);
	}
}

static gboolean
get_srv_record_services_sync (const gchar *domain,
			      GSList **out_srv_records,
			      GCancellable *cancellable,
			      GError **error)
{
	const gchar *known_services[] = {
		"imaps",
		"imap",
		"pop3s",
		"pop3",
		"submission",
		"caldavs",
		"caldav",
		"carddavs",
		"carddav",
		"ldaps",
		"ldap"
	};

	GResolver *resolver;
	gint ii;
	gboolean success = TRUE;

	g_return_val_if_fail (domain != NULL, FALSE);
	g_return_val_if_fail (out_srv_records != NULL, FALSE);

	resolver = g_resolver_get_default ();

	for (ii = 0; success && ii < G_N_ELEMENTS (known_services); ii++) {
		GList *targets;
		GError *local_error = NULL;

		targets = g_resolver_lookup_service (resolver, known_services[ii], "tcp", domain, cancellable, &local_error);

		if (local_error) {
			if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
				success = FALSE;
				g_propagate_error (error, local_error);
				break;
			}
			g_clear_error (&local_error);
		} else {
			GList *link;

			targets = g_srv_target_list_sort (targets);

			for (link = targets; link; link = g_list_next (link)) {
				GSrvTarget *target = link->data;
				const gchar *hostname;

				if (!target)
					continue;

				hostname = g_srv_target_get_hostname (target);
				if (!hostname || !*hostname)
					continue;

				*out_srv_records = g_slist_prepend (*out_srv_records,
					e_srv_record_new (known_services[ii],
							  hostname,
							  g_srv_target_get_port (target),
							  g_srv_target_get_priority (target),
							  g_srv_target_get_weight (target)));
			}

			g_list_free_full (targets, (GDestroyNotify) g_srv_target_free);
		}
	}

	g_object_unref (resolver);

	*out_srv_records = g_slist_reverse (*out_srv_records);

	return success;
}

int
main (int argc, char **argv)
{
	GSList *srv_records = NULL, *link;
	GError *error = NULL;
	const gchar *domain = "icloud.com";

	if (argc == 2)
		domain = argv[1];

	if (!get_srv_record_services_sync (domain, &srv_records, NULL, &error)) {
		g_printerr ("Failed to get all records, has only %d, error:'%s'\n", g_slist_length (srv_records), error ? error->message : "Unknown error");
		g_slist_free_full (srv_records, e_srv_record_free);
		return 1;
	}

	printf ("Got %d records:\n", g_slist_length (srv_records));
	for (link = srv_records; link; link = g_slist_next (link)) {
		ESrvRecord *srv = link->data;

		printf ("   %s: %s:%d priority:%d weight:%d\n",
			srv->service,
			srv->hostname,
			srv->port,
			srv->priority,
			srv->weight);
	}

	g_slist_free_full (srv_records, e_srv_record_free);

	return 0;
}
