/* This file is part of GNU Pies
Copyright (C) 2009, 2010, 2013, 2015 Sergey Poznyakoff
GNU Pies 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, or (at your option)
any later version.
GNU Pies 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.
You should have received a copy of the GNU General Public License
along with GNU Pies. If not, see . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include "libpies.h"
#include
/* scheme://[user[:password]@][host[:port]/]path[;arg=str[;arg=str...]
*/
static int
alloc_string_len (char **sptr, const char *start, size_t len)
{
*sptr = malloc (len + 1);
if (!*sptr)
return 1;
memcpy (*sptr, start, len);
(*sptr)[len] = 0;
return 0;
}
static int
alloc_string (char **sptr, const char *start, const char *end)
{
size_t len = end ? end - start : strlen (start);
return alloc_string_len (sptr, start, len);
}
static int
url_parse_args (struct pies_url *url, char **str)
{
struct wordsplit ws;
ws.ws_delim = ";";
if (wordsplit (*str, &ws, WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_DELIM))
return 1;
url->argc = ws.ws_wordc;
url->argv = ws.ws_wordv;
return 0;
}
static int
url_parse_path (struct pies_url *url, char **str)
{
char *p;
p = strchr (*str, ';');
if (!p)
p = *str + strlen (*str);
if (alloc_string (&url->path, *str, p))
return 1;
*str = p;
if (*p)
++*str;
return url_parse_args (url, str);
}
/* On input str points at the beginning of host part */
static int
url_parse_host (struct pies_url *url, char **str)
{
char *s = *str;
size_t len = strcspn (s, "/:");
if (s[len] == ':')
{
char *start = s + len + 1;
char *q;
unsigned long n = strtoul (start, &q, 10);
if (n > USHRT_MAX)
return 1;
if ((*q && !strchr ("/;", *q)))
{
char *proto = url->proto_s ? url->proto_s : "tcp";
size_t size = strcspn (start, "/;");
struct servent *serv;
alloc_string_len (&url->port_s, start, size);
serv = getservbyname (url->port_s, proto);
if (!serv)
return 1;
url->port = ntohs (serv->s_port);
*str = start + size;
}
else
{
alloc_string_len (&url->port_s, start, q - start);
url->port = n;
*str = q;
}
}
else
*str = s + len;
if (alloc_string_len (&url->host, s, len))
return 1;
if (**str)
{
if (*(*str)++ == '/')
return url_parse_path (url, str);
else
return url_parse_args (url, str);
}
return 0;
}
/* On input str points past the mech:// part */
static int
url_parse_user (struct pies_url *url, char **str)
{
if (**str == '/')
return url_parse_path (url, str);
else
{
size_t len = strcspn (*str, ":;@/");
char *p = *str + len;
switch (*p)
{
case ';':
case ':':
len = strcspn (p + 1, "@/:");
if (p[len + 1] == '@')
{
if (alloc_string_len (&url->passwd, p + 1, len))
return 1;
if (alloc_string (&url->user, *str, p))
return 1;
*str = p + len + 2;
}
break;
case '@':
if (alloc_string (&url->user, *str, p))
return 1;
url->passwd = NULL;
*str = p + 1;
}
return url_parse_host (url, str);
}
}
static int
url_parse_scheme (struct pies_url *url, const char *str)
{
size_t len;
char *p;
if (!str)
{
errno = EINVAL;
return 1;
}
len = strcspn (str, ":+");
if (!str[len])
{
errno = EINVAL;
return 1;
}
alloc_string_len (&url->scheme, str, len);
str += len;
if (*str == '+')
{
struct protoent *proto;
len = strcspn (++str, ":");
if (str[len] == 0)
{
errno = EINVAL;
return 1;
}
alloc_string_len (&url->proto_s, str, len);
str += len;
proto = getprotobyname (url->proto_s);
if (!proto)
{
errno = EINVAL;
return 1;
}
url->proto = proto->p_proto;
}
else
url->proto = 0;
/* Skip slashes */
p = (char*) str + 1;
if (memcmp (p, "//", 2))
{
errno = EINVAL;
return 1;
}
p += 2;
return url_parse_user (url, &p);
}
void
pies_url_free_user (struct pies_url *url)
{
if (url->user)
{
free (url->user);
url->user = NULL;
}
}
void
pies_url_free_passwd (struct pies_url *url)
{
if (url->passwd)
{
memset (url->passwd, 0, strlen (url->passwd));
free (url->passwd);
url->passwd = NULL;
}
}
void
pies_url_destroy (struct pies_url **purl)
{
int i;
if (purl && *purl)
{
struct pies_url *url = *purl;
free (url->string);
free (url->scheme);
free (url->host);
free (url->port_s);
free (url->proto_s);
free (url->path);
pies_url_free_user (url);
pies_url_free_passwd (url);
for (i = 0; i < url->argc; i++)
free (url->argv[i]);
free (url->argv);
free (url);
*purl = NULL;
}
}
static int
strasgn (char **dst, char *src)
{
if (!src)
*dst = NULL;
else
{
*dst = strdup (src);
if (!*dst)
return 1;
}
return 0;
}
static int
do_url_copy (struct pies_url *dest, struct pies_url *src)
{
#define STRASGN(a,b) if (strasgn (a, b)) return -1;
size_t i;
STRASGN (&dest->string, src->string);
STRASGN (&dest->scheme, src->scheme);
STRASGN (&dest->host, src->host);
STRASGN (&dest->port_s, src->port_s);
dest->port = src->port;
STRASGN (&dest->proto_s, src->proto_s);
dest->proto = src->proto;
STRASGN (&dest->path, src->path);
STRASGN (&dest->user, src->user);
STRASGN (&dest->passwd, src->passwd);
dest->argv = calloc (src->argc + 1, sizeof (dest->argv[0]));
if (!dest->argv)
return -1;
for (i = 0; i < src->argc; i++)
{
if (!(dest->argv[i] = strdup (src->argv[0])))
{
dest->argc = i;
return -1;
}
}
dest->argv[i] = NULL;
dest->argc = i;
return 0;
#undef STRASGN
}
int
pies_url_copy (struct pies_url **purl, struct pies_url *src)
{
struct pies_url *dest = malloc (sizeof (*dest));
if (!dest)
return -1;
memset (dest, 0, sizeof (*dest));
if (do_url_copy (dest, src))
{
pies_url_destroy (&dest);
return -1;
}
*purl = dest;
return 0;
}
int
pies_basic_url_create (struct pies_url **purl, const char *str)
{
int rc;
struct pies_url *url;
url = malloc (sizeof (*url));
if (!url)
return 1;
memset (url, 0, sizeof (*url));
rc = url_parse_scheme (url, str);
if (rc)
pies_url_destroy (&url);
else
{
url->string = strdup (str);
*purl = url;
}
return rc;
}
extern char **environ;
int
pies_url_create (struct pies_url **purl, const char *str)
{
struct wordsplit ws;
int rc;
ws.ws_env = (const char**) environ;
if (wordsplit (str, &ws,
WRDSF_NOCMD | WRDSF_QUOTE | WRDSF_NOSPLIT | WRDSF_ENV))
return -1;
rc = pies_basic_url_create (purl, ws.ws_wordv[0]);
wordsplit_free (&ws);
return rc;
}
const char *
pies_url_get_arg (struct pies_url *url, const char *argname)
{
int i;
size_t arglen = strlen (argname);
for (i = 0; i < url->argc; i++)
{
size_t len = strcspn (url->argv[i], "=");
if (len == arglen && memcmp (url->argv[i], argname, arglen) == 0)
return url->argv[i] + len + 1;
}
return NULL;
}