/*
   collection.c: management of a collection of connections, for sslh-select

# Copyright (C) 2021  Yves Rutschle
# 
# This program 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 program 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.
# 
# The full text for the General Public License is here:
# http://www.gnu.org/licenses/gpl.html

*/

#include "common.h"
#include "collection.h"
#include "sslh-conf.h"
#include "gap.h"

/* Info to keep track of all connections */
struct cnx_collection {
    gap_array* fd2cnx;  /* Array indexed by file descriptor to things in cnx[] */
};

/* Allocates and initialises a new collection of connections with at least
 * `len` elements. */
cnx_collection* collection_init(int len)
{
    cnx_collection* collection;

    collection = malloc(sizeof(*collection));
    CHECK_ALLOC(collection, "collection_init(collection)");

    memset(collection, 0, sizeof(*collection));

    collection->fd2cnx = gap_init(len);

    return collection;
}

/* Caveat: might not work, as has never been used */
void collection_destroy(cnx_collection* collection)
{
    /* Caveat 2: no code to free connections yet */
    gap_destroy(collection->fd2cnx);
    free(collection);
}

/* Points the file descriptor to the specified connection index */
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd)
{
    gap_set(collection->fd2cnx, fd, cnx);
    return 0;
}

/* Allocates a connection and inits it with specified file descriptor */
struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collection, int fd)
{
    struct connection* cnx = malloc(sizeof(*cnx));

    if (!cnx) return NULL;

    init_cnx(cnx);
    cnx->type = SOCK_STREAM;
    cnx->q[0].fd = fd;
    cnx->state = ST_PROBING;
    cnx->probe_timeout = time(NULL) + cfg.timeout;

    gap_set(collection->fd2cnx, fd, cnx);

    return cnx;
}

/* Remove a connection from the collection */
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx)
{
    if (cnx->q[0].fd != -1)
        gap_set(collection->fd2cnx, cnx->q[0].fd, NULL);
    if (cnx->q[1].fd != -1)
        gap_set(collection->fd2cnx, cnx->q[1].fd, NULL);
    free(cnx);
    return 0;
}

/* Returns the connection that contains the file descriptor */
struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd)
{
    return gap_get(collection->fd2cnx, fd);
}