/* This file is part of GNU Pies Copyright (C) 2009 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 "pies.h" #include #include #include #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_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); free (url->user); free (url->passwd); for (i = 0; i < url->argc; i++) free (url->argv[i]); free (url->argv); free (url); *purl = NULL; } } int pies_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; } 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; }