/*
 * RPC endpoint mapper
 *
 * Copyright (C) 2001 Ove Kåven, TransGaming Technologies Inc.
 */

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "unicode.h"
#include "request.h"
#include "process.h"
#include "server_memory.h"

#include "rpc.h"

struct epmap_entry
{
    struct epmap_entry *next;
    RPC_SYNTAX_IDENTIFIER iface;
    UUID object;
    char *protseq;
    char *endpoint;
};

/* Per process variables */
static const UUID nil_object;
/* End per process variables */

START_FILE_STATIC_VARIABLES( rpc_epmap )
  struct epmap_entry *epmap;
END_FILE_STATIC_VARIABLES( rpc_epmap )

static struct epmap_entry *find_endpoint(const RPC_SYNTAX_IDENTIFIER *iface,
                                         const char *protseq, const UUID *object)
{
    struct epmap_entry *map;
    for (map=SERVER_STATIC_VARIABLE( rpc_epmap, epmap ); map; map=map->next) {
        if (memcmp(&map->iface, iface, sizeof(RPC_SYNTAX_IDENTIFIER))) continue;
        if (memcmp(&map->object, object, sizeof(UUID))) continue;
        if (strcmp(map->protseq, protseq)) continue;
        return map;
    }
    return NULL;
}

static void register_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq,
                              const char *endpoint, const UUID *objects, int objcount,
                              int no_replace)
{
    int c;

    if (!objcount) {
        objects = &nil_object;
        objcount = 1;
    }

    for (c=0; c<objcount; c++) {
        struct epmap_entry *map = NULL;
        if (!no_replace)
            map = find_endpoint(iface, protseq, &objects[c]);
        if (map) {
            mem_free(map->endpoint);
        }
        else {
            map = mem_alloc(sizeof(struct epmap_entry));
            memcpy(&map->iface, iface, sizeof(RPC_SYNTAX_IDENTIFIER));
            memcpy(&map->object, &objects[c], sizeof(UUID));
            map->protseq = mem_strdup(protseq);
            map->next = SERVER_STATIC_VARIABLE( rpc_epmap, epmap );
            SERVER_STATIC_VARIABLE( rpc_epmap, epmap ) = map;
        }
        map->endpoint = mem_strdup(endpoint);
    }
}

static void unregister_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq,
                                const char *endpoint, const UUID *objects, int objcount)
{
    struct epmap_entry *map, *prev, *nprev, *next;
    int c;

    if (!objcount) {
        objects = &nil_object;
        objcount = 1;
    }

    for (prev=NULL,nprev=NULL,map=SERVER_STATIC_VARIABLE( rpc_epmap, epmap ),next=map?map->next:NULL;
         map; prev=nprev,map=next,next=map?map->next:NULL) {
        nprev = map;
        if (memcmp(&map->iface, iface, sizeof(RPC_SYNTAX_IDENTIFIER))) continue;
        for (c=0; c<objcount; c++)
            if (!memcmp(&map->object, &objects[c], sizeof(UUID))) break;
        if (c==objcount) continue;
        if (strcmp(map->protseq, protseq)) continue;

        if (prev) prev->next = map->next;
        else SERVER_STATIC_VARIABLE( rpc_epmap, epmap ) = map->next;
        nprev = prev;

        mem_free(map->protseq);
        mem_free(map->endpoint);
        mem_free(map);
    }
}

static void resolve_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq,
                             const UUID *object)
{
    size_t len;
    char *data;
    struct epmap_entry *map;

    if (!(map = find_endpoint(iface, protseq, object))) {
        set_error( EPT_NT_NOT_REGISTERED );
        return;
    }

    len = min( get_reply_max_size(), strlen(map->endpoint)+1 );
    if (len && ((data = set_reply_data_size(len))))
       memcpy(data, map->endpoint, len);
}

static const void *get_struct(const char**ptr, const char*end, size_t size)
{
    const char *data = *ptr;

    *ptr = data + size;
    if (*ptr > end) {
        set_error( STATUS_INVALID_PARAMETER );
        return NULL;
    }

    return data;
}

static const char *get_string(const char**ptr, const char*end)
{
    const char *str = *ptr, *nptr = str;

    while (nptr < end && *nptr) nptr++;
    if (nptr == end) {
        set_error( STATUS_INVALID_PARAMETER );
        return NULL;
    }
    *ptr = nptr + 1;

    return str;
}

DECL_HANDLER(register_rpc_endpoints)
{
#if 1
    const char *data = get_req_data();
    const char *end = data + get_req_data_size();
    const RPC_SYNTAX_IDENTIFIER *iface = get_struct(&data, end, sizeof(RPC_SYNTAX_IDENTIFIER));
    const UUID *objects = get_struct(&data, end, req->objects * sizeof(UUID));
#else
    size_t size = 0;
    size_t offset = 0;

    const RPC_SYNTAX_IDENTIFIER *iface;
    const UUID *objects;

    iface = get_req_data_offset(offset,&size);
    offset += sizeof(RPC_SYNTAX_IDENTIFIER);
    assert( size == (sizeof(UUID) + sizeof(RPC_SYNTAX_IDENTIFIER)) );

    objects = get_req_data_offset(offset,&size);
    offset += sizeof(UUID);
    assert( size == sizeof(UUID) );
#endif

    if (iface && objects) {
        int c;
        for (c=0; c<req->bindings; c++) {
            const char *protseq = get_string(&data, end);
            const char *endpoint = get_string(&data, end);
            if (protseq && endpoint)
                register_endpoint(iface, protseq, endpoint, objects, req->objects, req->no_replace);
        }
    }
}

DECL_HANDLER(unregister_rpc_endpoints)
{
#if 1
    const char *data = get_req_data();
    const char *end = data + get_req_data_size();
    const RPC_SYNTAX_IDENTIFIER *iface = get_struct(&data, end, sizeof(RPC_SYNTAX_IDENTIFIER));
    const UUID *objects = get_struct(&data, end, req->objects * sizeof(UUID));
#else
    size_t size = 0;
    size_t offset = 0;

    const RPC_SYNTAX_IDENTIFIER *iface;
    const UUID *objects;

    iface = get_req_data_offset(offset,&size);
    offset += sizeof(RPC_SYNTAX_IDENTIFIER);
    assert( size > (sizeof(UUID) + sizeof(RPC_SYNTAX_IDENTIFIER)) );

    objects = get_req_data_offset(offset,&size);
    offset += sizeof(UUID);
    assert( size > sizeof(UUID) );
    

#endif
    if (iface && objects) {
        int c;
        for (c=0; c<req->bindings; c++) {
            const char *protseq = get_string(&data, end);
            const char *endpoint = get_string(&data, end);
            if (protseq && endpoint)
                unregister_endpoint(iface, protseq, endpoint, objects, req->objects);
        }
    }
}

DECL_HANDLER(resolve_rpc_endpoint)
{
    size_t size = 0;
    size_t offset = 0;

    const RPC_SYNTAX_IDENTIFIER *iface;
    const UUID *object;
    const char *protseq;

    iface = get_req_data_offset(offset,&size);
    offset += sizeof(RPC_SYNTAX_IDENTIFIER);
    assert( size > (sizeof(UUID) + sizeof(RPC_SYNTAX_IDENTIFIER)) );

    object = get_req_data_offset(offset,&size);
    offset += sizeof(UUID);
    assert( size > sizeof(UUID) );

    protseq = get_req_data_offset(offset,&size);
    assert( size > 0 );

    if (iface && object && protseq) {
        resolve_endpoint(iface, protseq, object);
    }
}
