auth-client.c revision c38c5cd68f3f63f29fdbf8e773cb2a525e93a9bc
/* 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_modifyable_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);
}
}