/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "corbaloc.h"
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

static inline bool
get_xbit_number(const unsigned char bits,
                const char *num_str,
                unsigned long *ul)
{
        unsigned long max;

        switch (bits) {
        case 8 :
                max = 0xff;
                break;
        case 16 :
                max = 0xffff;
                break;
        case 32 :
                max = 0xffffffff;
                break;
        default :
                return false;
        }

        if (!num_str)
                return false;

        if (*num_str == '-')
                return false;

        char *tmp;
        unsigned long num = strtoul(num_str, &tmp, 10);
        if (errno == ERANGE)
                return false;

        if (*tmp != '\0')
                return false;

        if (num > max)
                return false;

        *ul = (unsigned long)num;

        return true;
}

// Will realloc and strcat src to *dest, freeing *dest if
// not successfull. Returns a pointer to the longer string.
static inline char*
append_string(char **dest,
              const char *src)
{
        char *retv = NULL;
        size_t size;


        if (!*dest) {
                size = sizeof(char) * (strlen(src) + 1);
                retv = (char*)calloc(size, sizeof(char));
        } else {
                size = sizeof(char) * (strlen(*dest) + strlen(src) + 1);
                retv = (char*)realloc((void*)*dest, size);
        }
        if (!retv) {
                free(*dest);
                return NULL;
        }

        strcat_s(retv, size, src);
        *dest = retv;

        return *dest;
}

// extracts port numbers from ListenEndpoint string
bool
get_ports(char *endpoint,
          unsigned short &iiop_port,
          unsigned short &ssl_port)
{
        if (!endpoint)
                return false;

        unsigned long n;
        char *pos = endpoint + 5*(sizeof(char));
        char *tmp;
        bool ssl = true;


        while (*(pos++) != ':')
                ;
        if (!pos)
                return false;

        tmp = pos;
        while (*tmp && (*(++tmp) != '/'))
                ;
        if (!*tmp) {
                ssl = false;
                ssl_port = 0;
        } else
                *tmp = '\0';

        // get IIOP port
        if (get_xbit_number(16, pos, &n))
                iiop_port = (unsigned short)n;
        else
                goto out;

        if (!ssl)
                return true;

        pos = ++tmp;
        while (*pos && (*(pos++) != '='))
                ;

        // get SSL port
        if (get_xbit_number(16, pos, &n))
                ssl_port = (unsigned short)n;
        else
                goto out;

        return true;

out:
        return false;
}

// Build corbaloc string or return NULL on error
char*
corbaloc_str(const char *IORTable_name,
             const char *hostname,
             const unsigned short port)
{
        if (!IORTable_name)
                return NULL;
        if (!hostname)
                return NULL;
        if (!port)
                return NULL;

        char *retv = NULL;
        char brutus_port[32] = { '\0' };
        if (0 >= _snprintf_s(brutus_port, sizeof(brutus_port), sizeof(brutus_port), "%hu", port))
                goto out;

        if (!append_string(&retv, "corbaloc:iiop:1.2@"))
                goto out;
        if (!append_string(&retv, hostname))
                goto out;
        if (!append_string(&retv, ":"))
                goto out;
        if (!append_string(&retv, (const char*)brutus_port))
                goto out;
        if (!append_string(&retv, "/"))
                goto out;
        append_string(&retv, IORTable_name);

out:
        return retv;
}
