view src/lib-auth/auth-client.c @ 4891:6ab2712f1a93 HEAD

Only imap binary was actually working.
author Timo Sirainen <tss@iki.fi>
date Sun, 10 Dec 2006 14:35:02 +0200
parents 1a35d53c18fc
children 204d7edc7cdc
line wrap: on
line source

/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "buffer.h"
#include "ioloop.h"
#include "hash.h"
#include "auth-client.h"
#include "auth-server-connection.h"

#include <dirent.h>
#include <sys/stat.h>

struct auth_client *auth_client_new(unsigned int client_pid)
{
	return auth_client_new_external(client_pid, NULL, NULL, NULL);
}

struct auth_client *auth_client_new_external(unsigned int client_pid,
					     const char *socket_paths,
					     input_func_add_t *add_func,
					     input_func_remove_t *remove_func)
{
	struct auth_client *client;

	client = i_new(struct auth_client, 1);
	client->pid = client_pid;
	client->socket_paths = i_strdup(socket_paths);
	client->available_auth_mechs = buffer_create_dynamic(default_pool, 128);

	client->ext_input_add = add_func;
	client->ext_input_remove = remove_func;

	auth_client_connect_missing_servers(client);
	return client;
}

void auth_client_free(struct auth_client **_client)
{
	struct auth_client *client = *_client;
	struct auth_server_connection *next;
	struct auth_mech_desc *mech;
	size_t i, size;

	*_client = NULL;

	mech = buffer_get_modifiable_data(client->available_auth_mechs, &size);
	size /= sizeof(*mech);
	for (i = 0; i < size; i++)
		i_free(mech[i].name);
	buffer_free(client->available_auth_mechs);

	while (client->connections != NULL) {
		next = client->connections->next;
		auth_server_connection_destroy(&client->connections, FALSE);
		client->connections = next;
	}

	if (client->to_reconnect != NULL)
		timeout_remove(&client->to_reconnect);
	i_free(client->socket_paths);
	i_free(client);
}

const struct auth_mech_desc *
auth_client_get_available_mechs(struct auth_client *client,
				unsigned int *mech_count)
{
	const struct auth_mech_desc *mechs;
	size_t size;

	mechs = buffer_get_data(client->available_auth_mechs, &size);
	*mech_count = size / sizeof(*mechs);
	return mechs;
}

const struct auth_mech_desc *
auth_client_find_mech(struct auth_client *client, const char *name)
{
	const struct auth_mech_desc *mech;
	size_t i, size;

	mech = buffer_get_data(client->available_auth_mechs, &size);
	size /= sizeof(*mech);
	for (i = 0; i < size; i++) {
		if (strcasecmp(mech[i].name, name) == 0)
			return &mech[i];
	}

	return NULL;
}

bool auth_client_reserve_connection(struct auth_client *client,
				    const char *mech,
				    struct auth_connect_id *id_r)
{
	struct auth_server_connection *conn;
	const char *error;

	conn = auth_server_connection_find_mech(client, mech, &error);
	if (conn == NULL)
		return FALSE;

	id_r->server_pid = conn->server_pid;
	id_r->connect_uid = conn->connect_uid;

	return TRUE;
}

bool auth_client_is_connected(struct auth_client *client)
{
	return !client->reconnect &&
		client->conn_waiting_handshake_count == 0 &&
		client->connections != NULL;
}

void auth_client_set_connect_notify(struct auth_client *client,
				    auth_connect_notify_callback_t *callback,
				    void *context)
{
	client->connect_notify_callback = callback;
	client->connect_notify_context = context;
}

static void reconnect_timeout(void *context)
{
	struct auth_client *client = context;

	auth_client_connect_missing_servers(client);
}

static void auth_client_connect_missing_servers_list(struct auth_client *client,
						     const char *list)
{
	const char *const *path;

	client->reconnect = FALSE;

	t_push();
	path = t_strsplit(list, ":");
	for (; *path != NULL; path++) {
		if (auth_server_connection_find_path(client, *path) == NULL) {
			if (auth_server_connection_new(client, *path) == NULL)
				client->reconnect = TRUE;
		}
	}
	t_pop();
}

void auth_client_connect_missing_servers(struct auth_client *client)
{
	DIR *dirp;
	struct dirent *dp;
	struct stat st;

	if (client->socket_paths != NULL) {
		auth_client_connect_missing_servers_list(client,
							 client->socket_paths);
	} else {
		/* we're chrooted */
		dirp = opendir(".");
		if (dirp == NULL) {
			i_fatal("opendir(.) failed when trying to get list of "
				"authentication servers: %m");
		}

		client->reconnect = FALSE;
		while ((dp = readdir(dirp)) != NULL) {
			const char *name = dp->d_name;

			if (name[0] == '.')
				continue;

			if (auth_server_connection_find_path(client,
							     name) != NULL) {
				/* already connected */
				continue;
			}

			/* Normally they're sockets, but in UnixWare they're
			   created as fifos. */
			if (stat(name, &st) == 0 &&
			    (S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode))) {
				if (auth_server_connection_new(client,
							       name) == NULL)
					client->reconnect = TRUE;
			}
		}

		if (closedir(dirp) < 0)
			i_error("closedir() failed: %m");
	}

	if (client->reconnect || client->connections == NULL) {
		if (client->to_reconnect == NULL &&
		    client->ext_input_add == NULL) {
			client->to_reconnect =
				timeout_add(5000, reconnect_timeout, client);
		}
	} else if (client->to_reconnect != NULL)
		timeout_remove(&client->to_reconnect);

	if (client->connect_notify_callback != NULL) {
		client->connect_notify_callback(client,
				auth_client_is_connected(client),
				client->connect_notify_context);
	}
}