/* url.c - URL encoding
 *
 * Copyright (C) 2005, 2007  Oskar Liljeblad
 *
 * 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 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include <config.h>
#include <stdbool.h>		/* Gnulib, POSIX, C99 */
#include <stdint.h>		/* Gnulib, POSIX, C99 */
#include <string.h>		/* C89 */
#include <stdlib.h>		/* C89 */
#include <ctype.h>
#include "xalloc.h"		/* Gnulib */
#include "xstrndup.h"		/* Gnulib */
#include "gmediaserver.h"
#include "intutil.h"

#define HEXCHRLC(x) ((x) >= 10 ? 'a'+(x) : '0'+(x))
#define HEXCHRUC(x) ((x) >= 10 ? 'A'+(x) : '0'+(x))

void
free_url(URL *url)
{
    free(url->hostname);
    free(url->path);
    free(url);
}

/* Parse an http URL that does not have logon information (user and
 * possibly password).
 * This function really needs to be rewritten. Perhaps we can create 
 * a lexer to this?
 */
URL *
parse_simple_url(const char *urlstr)
{
    char *next;
    char *next2;
    char *hostname;
    uint16_t port;
    char *path;
    URL *url;

    if (strncasecmp(urlstr, "http://", 7) != 0)
        return NULL;

    next = strpbrk(urlstr+7, ":/");
    if (next == NULL) {
        hostname = xstrdup(urlstr+7);
        port = 80;
        path = xstrdup("/");
    } else if (*next == ':') {
        next2 = strchr(next+1, '/');
        if (next2 == NULL) {
            if (!parse_uint16(next+1, &port))
                return NULL;
            path = xstrdup("/");
        } else {
            char *tmp = xstrndup(next+1, next2-next-1);
            if (!parse_uint16(tmp, &port)) { /* XXX: should make sure tmp[0]=='\0' is not accepted */
                free(tmp);
                return NULL;
            }
            free(tmp);
            path = xstrdup(next2);
        }
        hostname = xstrndup(urlstr+7, next-urlstr-7);
    } else {
        port = 80;
        hostname = xstrndup(urlstr+7, next-urlstr-7);
        path = xstrdup(next);
    }
    
    url = xmalloc(sizeof(URL));
    url->port = port;
    url->hostname = hostname;
    url->path = path;

    return url;
}

void
sgmlescape(const char *str, char *target, uint32_t *length)
{
    if (target != NULL) {
        uint32_t len = 0;

        for (; *str; str++) {
            if (*str == '<') {
                memcpy(target+len, "&lt;", 4);
		len += 4;
	    } else if (*str == '>') {
                memcpy(target+len, "&gt;", 4);
		len += 4;
	    } else if (*str == '&') {
                memcpy(target+len, "&amp;", 5);
		len += 5;
            } else {
                target[len++] = *str;
            }
        }
	target[len] = '\0';

        if (length != NULL)
            *length = len;
    }
    else if (length != NULL) {
        uint32_t len = 0;

        for (; *str; str++) {
            if (*str == '<') {
		len += 4;
	    } else if (*str == '>') {
		len += 4;
	    } else if (*str == '&') {
		len += 5;
            } else {
                len++;
            }
        }

        *length = len;
    }
}

char *
xsgmlescape(const char *str)
{
    uint32_t len;
    char *out;
    
    sgmlescape(str, NULL, &len);
    out = xmalloc(len+1);
    sgmlescape(str, out, NULL);
    return out;
}

void
urlencode(const char *str, char *target, uint32_t *length)
{
    if (target != NULL) {
        uint32_t len = 0;

        for (; *str; str++) {
            if (!isalnum(*str) && *str != '/') {
                target[len++] = '%';
                target[len++] = HEXCHRUC(*str >> 4);
                target[len++] = HEXCHRUC(*str & 0x0F);
            } else {
                target[len++] = *str;
            }
        }
	target[len] = '\0';

        if (length != NULL)
            *length = len;
    }
    else if (length != NULL) {
        uint32_t len = 0;

        for (; *str; str++) {
            if (!isalnum(*str) && *str != '/') {
                len += 3;
            } else {
                len++;
            }
        }

        *length = len;
    }
}

char *
xurlencode(const char *str)
{
    uint32_t len;
    char *out;
    
    urlencode(str, NULL, &len);
    out = xmalloc(len+1);
    urlencode(str, out, NULL);
    return out;
}
