#include <stdlib.h>
#include <netinfo/ni.h>
#include <string.h>
#include <ctype.h>
#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>
extern void *_ni_dup(void *);
static const char *
eatslash(
const char *path
)
{
while (*path == '/') {
path++;
}
return (path);
}
static void
unescape(
ni_name *name
)
{
ni_name newname;
ni_name p;
int len;
int i;
p = *name;
len = strlen(p);
newname = malloc(len + 1);
for (i = 0; *p != 0; i++) {
if (*p == '\\') {
p++;
}
newname[i] = *p++;
}
ni_name_free(name);
newname[i] = 0;
*name = newname;
}
static const char *
escindex(
const char *str,
char ch
)
{
char *p;
p = index(str, ch);
if (p == NULL) {
return (NULL);
}
if (p == str) {
return (p);
}
if (p[-1] == '\\') {
return (escindex(p + 1, ch));
}
return (p);
}
static void
setstuff(
void *ni,
ni_fancyopenargs *args
)
{
if (args != NULL) {
ni_setabort(ni, args->abort);
if (args->rtimeout) {
ni_setreadtimeout(ni, args->rtimeout);
}
if (args->wtimeout) {
ni_setwritetimeout(ni, args->wtimeout);
}
}
}
static void *
ni_relopen(
void *ni,
const char *domain,
int freeold,
ni_fancyopenargs *args
)
{
void *newni;
void *tmpni;
char *start;
char *slash;
char *component;
start = (char *)domain;
while ((slash = (char *)escindex(start, '/')) != NULL) {
start = slash + 1;
}
if (index(start, '@') != NULL) {
component = ni_name_dup(start);
newni = ni_new(NULL, component);
free(component);
if (newni != NULL && args != NULL)
setstuff(newni, args);
if (ni != NULL && freeold)
ni_free(ni);
return (newni);
}
component = ni_name_dup(domain);
slash = (char *)escindex(component, '/');
if (slash != NULL) {
*slash = 0;
}
unescape(&component);
tmpni = NULL;
if (ni != NULL && args != NULL) {
tmpni = _ni_dup(ni);
if (freeold) {
ni_free(ni);
}
ni = tmpni;
setstuff(ni, args);
}
newni = ni_new(ni, component);
free(component);
if (tmpni != NULL) {
ni_free(ni);
ni = NULL;
}
if (ni != NULL && freeold) {
ni_free(ni);
}
if (newni == NULL) {
return (NULL);
}
setstuff(newni, args);
ni = newni;
if (slash != NULL) {
slash = (char *)escindex(domain, '/');
domain = eatslash(slash + 1);
return (ni_relopen(ni, domain, TRUE, NULL));
} else {
return (ni);
}
}
static void *
ni_rootopen(
ni_fancyopenargs *args
)
{
void *ni;
void *newni;
ni = ni_new(NULL, ".");
if (ni == NULL) {
return (NULL);
}
setstuff(ni, args);
for (;;) {
newni = ni_new(ni, "..");
if (newni == NULL) {
break;
}
ni_free(ni);
ni = newni;
}
return (ni);
}
static ni_name
ni_name_dupn(
ni_name_const start,
ni_name_const stop
)
{
int len;
ni_name new;
if (stop != NULL) {
len = stop - start;
} else {
len = strlen(start);
}
new = malloc(len + 1);
bcopy(start, new, len);
new[len] = 0;
return (new);
}
static ni_status
ni_relsearch(
void *ni,
const char *path,
ni_id *id
)
{
char *slash;
char *equal;
ni_name key;
ni_name val;
ni_idlist idl;
ni_status status;
slash = (char *)escindex(path, '/');
equal = (char *)escindex(path, '=');
if (equal != NULL && (slash == NULL || equal < slash)) {
key = ni_name_dupn(path, equal);
val = ni_name_dupn(equal + 1, slash);
} else {
if (equal == NULL || (slash != NULL && slash < equal)) {
key = ni_name_dup("name");
val = ni_name_dupn(path, slash);
} else {
key = ni_name_dupn(path, equal);
val = ni_name_dupn(equal + 1, slash);
}
}
unescape(&key);
unescape(&val);
NI_INIT(&idl);
status = ni_lookup(ni, id, key, val, &idl);
if (status != NI_OK) {
ni_name_free(&key);
ni_name_free(&val);
return (status);
}
id->nii_object = idl.niil_val[0];
ni_name_free(&key);
ni_name_free(&val);
ni_idlist_free(&idl);
if (slash == NULL) {
ni_self(ni, id);
return (NI_OK);
}
path = eatslash(slash);
return (ni_relsearch(ni, path, id));
}
ni_status
ni_open(
void *ni,
const char *domain,
void **newni
)
{
return (ni_fancyopen(ni, domain, newni, NULL));
}
ni_status
ni_fancyopen(
void *ni,
const char *domain,
void **newni,
ni_fancyopenargs *args
)
{
void *tmp = NULL;
int rootopen = 0;
if (*domain == '/') {
tmp = ni_rootopen(args);
if (tmp == NULL) {
return (NI_FAILED);
}
domain = eatslash(domain);
ni = tmp;
rootopen++;
}
if (*domain != 0) {
tmp = ni_relopen(ni, domain, FALSE, args);
if (rootopen) {
ni_free(ni);
}
}
if (tmp == NULL) {
return (NI_FAILED);
}
*newni = tmp;
ni_needwrite(*newni, args == NULL ? 0 : args->needwrite);
return (NI_OK);
}
ni_status
ni_pathsearch(
void *ni,
ni_id *id,
const char *path
)
{
ni_status status;
if (*path == '/') {
status = ni_root(ni, id);
if (status != NI_OK) {
return (status);
}
}
path = eatslash(path);
if (*path != 0) {
status = ni_relsearch(ni, path, id);
if (status != NI_OK) {
return (status);
}
}
return (NI_OK);
}
static char **
_ni_append_string(char *s, char **l)
{
int i, len;
if (s == NULL) return l;
if (l == NULL)
{
l = (char **)malloc(2 * sizeof(char *));
l[0] = strdup(s);
l[1] = NULL;
return l;
}
for (i = 0; l[i] != NULL; i++);
len = i + 1;
l = (char **)realloc(l, (len + 1) * sizeof(char *));
l[len - 1] = strdup(s);
l[len] = NULL;
return l;
}
static char **
_ni_explode_string(char *s, char c)
{
char **l = NULL;
char *p, *t;
int i, n;
if (s == NULL) return NULL;
p = s;
while (p[0] != '\0')
{
for (i = 0; ((p[i] != '\0') && p[i] != c); i++);
n = i;
t = malloc(n + 1);
for (i = 0; i < n; i++) t[i] = p[i];
t[n] = '\0';
l = _ni_append_string(t, l);
free(t);
t = NULL;
if (p[i] == '\0') return l;
if (p[i + 1] == '\0') l = _ni_append_string("", l);
p = p + i + 1;
}
return l;
}
static void
_ni_free_list(char **l)
{
int i;
if (l == NULL) return;
for (i = 0; l[i] != NULL; i++)
{
if (l[i] != NULL) free(l[i]);
l[i] = NULL;
}
if (l != NULL) free(l);
}
ni_status
ni_host_domain(char *host, char *domspec, void **domain)
{
void *d0, *d1;
struct sockaddr_in server;
ni_name tag;
int i, is_tag, is_local, is_relative;
ni_status status;
char **path;
struct hostent *h;
is_local = 1;
if (host == NULL)
{
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
else
{
is_local = 0;
server.sin_addr.s_addr = inet_addr(host);
if (server.sin_addr.s_addr == -1)
{
h = gethostbyname(host);
if (h == NULL)
{
*domain = (void *)NULL;
return NI_CANTFINDADDRESS;
}
bcopy(h->h_addr_list[0], &server.sin_addr.s_addr, h->h_length);
}
}
is_relative = 1;
is_tag = 1;
if (domspec == NULL)
{
is_tag = 0;
is_relative = 0;
}
else if (domspec[0] == '/')
{
is_tag = 0;
is_relative = 0;
}
else if (!strcmp(domspec, ".")) is_tag = 0;
else if (!strcmp(domspec, "..")) is_tag = 0;
else if (!strncmp(domspec, "./", 2)) is_tag = 0;
else if (!strncmp(domspec, "../", 3)) is_tag = 0;
if (is_tag == 1)
{
d0 = ni_connect(&server, domspec);
status = ni_addrtag(d0, &server, &tag);
ni_name_free(&tag);
if (status != NI_OK)
{
*domain = (void *)NULL;
return NI_FAILED;
}
*domain = d0;
return NI_OK;
}
if (is_local)
{
if (domspec == NULL) status = ni_open(NULL, ".", domain);
else status = ni_open(NULL, domspec, domain);
return status;
}
d0 = ni_connect(&server, "local");
status = ni_addrtag(d0, &server, &tag);
ni_name_free(&tag);
if (status != NI_OK)
{
*domain = (void *)NULL;
return NI_FAILED;
}
if ((domspec == NULL) || (!strcmp(domspec, ".")))
{
*domain = d0;
return NI_OK;
}
if (is_relative == 1)
{
path = _ni_explode_string(domspec, '/');
}
else
{
path = _ni_explode_string(domspec + 1, '/');
status = NI_OK;
while (status == NI_OK)
{
status = ni_open(d0, "..", &d1);
if (status == NI_OK)
{
ni_free(d0);
d0 = d1;
}
}
if (!strcmp(domspec, "/"))
{
*domain = d0;
return NI_OK;
}
}
for (i = 0; path[i] != NULL; i++)
{
status = ni_open(d0, path[i], &d1);
if (status != NI_OK)
{
_ni_free_list(path);
*domain = (void *)NULL;
return NI_FAILED;
}
ni_free(d0);
d0 = d1;
}
_ni_free_list(path);
*domain = d0;
return NI_OK;
}
static void
_ni_parse_url_hostspec(char *s, char **u, char **p, char **h)
{
char *p_at, *p_colon;
int ulen, plen, hlen;
if (s == NULL) return;
if (s[0] == '\0') return;
p_at = strchr(s, '@');
if (p_at == NULL)
{
hlen = strlen(s);
if (hlen == 0) return;
*h = malloc(hlen + 1);
strcpy(*h, s);
return;
}
*p_at = '\0';
p_at++;
hlen = strlen(p_at);
if (hlen > 0)
{
*h = malloc(hlen + 1);
strcpy(*h, p_at);
}
if (s[0] == '\0') return;
p_colon = strchr(s, ':');
if (p_colon == NULL)
{
ulen = strlen(s);
if (ulen == 0) return;
*u = malloc(ulen + 1);
strcpy(*u, s);
return;
}
*p_colon = '\0';
p_colon++;
plen = strlen(p_colon);
if (plen > 0)
{
*p = malloc(plen + 1);
strcpy(*p, p_colon);
}
ulen = strlen(s);
if (ulen > 0)
{
*u = malloc(ulen + 1);
strcpy(*u, s);
}
}
void
ni_parse_url(char *url, char **user, char **password, char **host,
char **domspec, char **dirspec)
{
int i, x, len;
char *str;
*host = NULL;
*user = NULL;
*password = NULL;
*domspec = NULL;
*dirspec = NULL;
x = strlen("netinfo://");
if (strncmp(url, "netinfo://", x)) return;
len = 0;
for (i = x; (url[i] != '\0') && (url[i] != '/'); i++) len++;
if (len != 0)
{
str = malloc(len + 1);
bcopy(url + x, str, len);
str[len] = '\0';
_ni_parse_url_hostspec(str, user, password, host);
free(str);
}
if (url[i] != '\0') i++;
x = i;
len = 0;
for (; (url[i] != '\0') && (url[i] != ':'); i++) len++;
if (len > 0)
{
*domspec = malloc(len + 1);
bcopy(url + x, *domspec, len);
(*domspec)[len] = '\0';
}
if (url[i] != '\0') i++;
x = i;
len = 0;
for (; url[i] != '\0'; i++) len++;
if (len > 0)
{
*dirspec = malloc(len + 1);
bcopy(url + x, *dirspec, len);
(*dirspec)[len] = '\0';
}
}
ni_status
ni_url(char *url, void **domain, ni_id *dir)
{
int nilen;
char *user, *password, *host;
char *domspec, *dirspec;
ni_status status;
nilen = strlen("netinfo://");
if (strncmp(url, "netinfo://", nilen))
{
*domain = (void *)NULL;
return NI_CANTFINDADDRESS;
}
ni_parse_url(url, &user, &password, &host, &domspec, &dirspec);
status = ni_host_domain(host, domspec, domain);
if (host != NULL) free(host);
if (domspec != NULL) free(domspec);
if (status != NI_OK)
{
if (user != NULL) free(user);
if (password != NULL) free(password);
if (dirspec != NULL) free(dirspec);
return status;
}
if (user != NULL)
{
ni_setuser(*domain, user);
free(user);
}
if (password != NULL)
{
ni_setpassword(*domain, password);
free(password);
}
if (dirspec == NULL)
{
status = ni_root(*domain, dir);
return status;
}
if ((dirspec[0] >= '0') && (dirspec[0] <= '9'))
{
dir->nii_object = atoi(dirspec);
free(dirspec);
status = ni_self(*domain, dir);
return status;
}
status = ni_pathsearch(*domain, dir, dirspec);
free(dirspec);
return status;
}