/* This file is part of GNU Pies Copyright (C) 2009-2010, 2013, 2015, 2017 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; }