diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 23 | ||||
-rw-r--r-- | lib/addrfmt.c | 174 | ||||
-rw-r--r-- | lib/grecsasrt.c | 78 | ||||
-rw-r--r-- | lib/grecsasrt.h | 22 | ||||
-rw-r--r-- | lib/libpies.h | 66 | ||||
-rw-r--r-- | lib/mkfilename.c | 42 | ||||
-rw-r--r-- | lib/netrc.c | 217 | ||||
-rw-r--r-- | lib/pp.c | 71 | ||||
-rw-r--r-- | lib/split3.c | 83 | ||||
-rw-r--r-- | lib/url.c | 360 |
10 files changed, 1119 insertions, 17 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 89812ca..a0d6614 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ # This file is part of GNU Pies. -# Copyright (C) 2005, 2006, 2007, 2008, 2010, 2013 Sergey Poznyakoff +# Copyright (C) 2005-2008, 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 @@ -16,15 +16,28 @@ noinst_LIBRARIES=libpies.a -noinst_HEADERS = libpies.h +noinst_HEADERS = libpies.h grecsasrt.h libpies_a_SOURCES=\ + addrfmt.c\ arraymember.c\ + grecsasrt.c\ + mkfilename.c\ + netrc.c\ parsetime.c\ proctitle.c\ - strtotok.c + pp.c\ + split3.c\ + strtotok.c\ + url.c -libpies_a_LIBADD=$(LIBOBJS) +libpies_a_LIBADD=\ + $(LIBOBJS)\ + @GRECS_LDADD@ -AM_CPPFLAGS = -I$(top_srcdir)/gnu -I../gnu +AM_CPPFLAGS = \ + -I$(top_srcdir)/gnu\ + -I../gnu\ + @GRECS_INCLUDES@\ + -DDEFAULT_PREPROCESSOR="$(DEFAULT_PREPROCESSOR)" diff --git a/lib/addrfmt.c b/lib/addrfmt.c new file mode 100644 index 0000000..ff615dd --- /dev/null +++ b/lib/addrfmt.c @@ -0,0 +1,174 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "libpies.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +int +str2port (char *str) +{ + struct servent *serv; + char *p; + int port; + + /* First try to read it from /etc/services */ + serv = getservbyname (str, "tcp"); + + if (serv != NULL) + port = ntohs (serv->s_port); + else + { + unsigned long l; + /* Not in services, maybe a number? */ + l = strtoul (str, &p, 0); + + if (*p || l < 0 || l > USHRT_MAX) + return -1; + + port = l; + } + + return port; +} + +static size_t +_my_stpcpy (char **pbuf, size_t *psize, const char *src) +{ + size_t slen = strlen (src); + if (pbuf == NULL || *pbuf == NULL) + return slen; + else + { + char *buf = *pbuf; + size_t size = *psize; + if (size > slen) + size = slen; + memcpy (buf, src, size); + *psize -= size; + *pbuf += size; + if (*psize) + **pbuf = 0; + else + (*pbuf)[-1] = 0; + return size; + } +} + +#define S_UN_NAME(sa, salen) \ + ((salen < offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path) + +static size_t +format_uint (char **bufptr, size_t *buflen, unsigned n) +{ + char *buf = *bufptr; + size_t len = *buflen; + size_t i; + + if (buf && len == 0) + return 0; + + for (i = 0;;) + { + unsigned x = n % 10; + if (buf) + { + if (len == 1) + break; + *buf++ = x + '0'; + --len; + } + n /= 10; + ++i; + if (n == 0) + break; + } + + if (buf) + { + char *p = *bufptr; + *bufptr = buf; + *buf = 0; + while (--buf > p) + { + char c = *p; + *p++ = *buf; + *buf = c; + } + } + return i; +} + +void +sockaddr_to_str (const struct sockaddr *sa, int salen, + char *bufptr, size_t buflen, + size_t *plen) +{ + size_t len = 0; + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in s_in = *(struct sockaddr_in *)sa; + len += _my_stpcpy (&bufptr, &buflen, inet_ntoa (s_in.sin_addr)); + len += _my_stpcpy (&bufptr, &buflen, ":"); + len += format_uint (&bufptr, &buflen, ntohs (s_in.sin_port)); + break; + } + + case AF_UNIX: + { + struct sockaddr_un *s_un = (struct sockaddr_un *)sa; + if (S_UN_NAME(s_un, salen)[0] == 0) + len += _my_stpcpy (&bufptr, &buflen, "anonymous socket"); + else + { + len += _my_stpcpy (&bufptr, &buflen, "socket "); + len += _my_stpcpy (&bufptr, &buflen, s_un->sun_path); + } + break; + } + + default: + len += _my_stpcpy (&bufptr, &buflen, "{Unsupported family: "); + len += format_uint (&bufptr, &buflen, sa->sa_family); + len += _my_stpcpy (&bufptr, &buflen, "}"); + } + if (plen) + *plen = len + 1; +} + +char * +sockaddr_to_astr (const struct sockaddr *sa, int salen) +{ + size_t size; + char *p; + + sockaddr_to_str (sa, salen, NULL, 0, &size); + p = malloc (size); + if (!p) + grecs_alloc_die (); + sockaddr_to_str (sa, salen, p, size, NULL); + return p; +} diff --git a/lib/grecsasrt.c b/lib/grecsasrt.c new file mode 100644 index 0000000..29bd0bf --- /dev/null +++ b/lib/grecsasrt.c @@ -0,0 +1,78 @@ +/* This file is part of GNU Pies + Copyright (C) 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <errno.h> +#include <grecs.h> +#include "libpies.h" +#include "grecsasrt.h" + +int +assert_grecs_value_type (grecs_locus_t *locus, + const grecs_value_t *value, int type) +{ + if (GRECS_VALUE_EMPTY_P (value)) + { + grecs_error (locus, 0, _("expected %s"), + grecs_data_type_string (type)); + return 1; + } + if (value->type != type) + { + grecs_error (locus, 0, _("expected %s, but found %s"), + grecs_data_type_string (type), + grecs_data_type_string (value->type)); + return 1; + } + return 0; +} + +int +assert_scalar_stmt (grecs_locus_t *locus, enum grecs_callback_command cmd) +{ + if (cmd != grecs_callback_set_value) + { + grecs_error (locus, 0, _("unexpected block statement")); + return 1; + } + return 0; +} + +int +conf_callback_url (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data) +{ + struct pies_url *url; + + if (assert_scalar_stmt (locus, cmd) + || assert_grecs_value_type (locus, value, GRECS_TYPE_STRING)) + return 1; + if (pies_url_create (&url, value->v.string)) + { + grecs_error (locus, 0, _("%s: cannot create URL: %s"), + value->v.string, strerror (errno)); + return 0; + } + if (varptr) + *(struct pies_url **) varptr = url; + else + pies_url_destroy (&url); + return 0; +} + diff --git a/lib/grecsasrt.h b/lib/grecsasrt.h new file mode 100644 index 0000000..bf75731 --- /dev/null +++ b/lib/grecsasrt.h @@ -0,0 +1,22 @@ +/* This file is part of GNU Pies. + Copyright (C) 2009, 2010, 2011, 2013 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 <http://www.gnu.org/licenses/>. */ + +int assert_grecs_value_type (grecs_locus_t *locus, + const grecs_value_t *value, int type); +int assert_scalar_stmt (grecs_locus_t *locus, enum grecs_callback_command cmd); +int conf_callback_url (enum grecs_callback_command cmd, + grecs_locus_t *locus, + void *varptr, grecs_value_t *value, void *cb_data); diff --git a/lib/libpies.h b/lib/libpies.h index b9c8af7..dab36df 100644 --- a/lib/libpies.h +++ b/lib/libpies.h @@ -1,5 +1,5 @@ /* This file is part of GNU Pies. - Copyright (C) 2009, 2010, 2011, 2013 Sergey Poznyakoff + Copyright (C) 2009, 2010, 2011, 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 @@ -14,11 +14,14 @@ You should have received a copy of the GNU General Public License along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */ +#include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <gettext.h> +#include <netinet/in.h> +#include <sys/un.h> +#include <grecs.h> - #if defined HAVE_SYSCONF && defined _SC_OPEN_MAX # define getmaxfd() sysconf(_SC_OPEN_MAX) #elif defined (HAVE_GETDTABLESIZE) @@ -26,22 +29,12 @@ #else # define getmaxfd() 256 #endif - - - -void *xmalloc (size_t size); -void *xzalloc (size_t size); -void *xcalloc (size_t count, size_t size); -char *xstrdup (const char *str); - void mf_proctitle_init (int argc, char *argv[], char *env[]); void mf_proctitle_format (const char *fmt, ...); - size_t longtostr (long i, char *buf, size_t size); size_t ulongtostr (unsigned long i, char *buf, size_t size); - struct tokendef { @@ -58,4 +51,53 @@ int strtotok_ci (struct tokendef *tab, const char *str, int *pres); int toktostr (struct tokendef *tab, int tok, const char **pres); int is_array_member (char * const * ar, char const *str); +int strsplit3 (const char *input, char *result[3], int flag); + +/* url.c */ +struct pies_url +{ + char *string; + char *scheme; + char *host; + char *port_s; + int port; + char *proto_s; + int proto; + char *path; + char *user; + char *passwd; + int argc; + char **argv; +}; + +int pies_url_create (struct pies_url **purl, const char *str); +void pies_url_destroy (struct pies_url **purl); +const char *pies_url_get_arg (struct pies_url *url, const char *argname); +int pies_url_copy (struct pies_url **purl, struct pies_url *src); +void pies_url_free_user (struct pies_url *url); +void pies_url_free_passwd (struct pies_url *url); + +void netrc_scan (struct pies_url *url); + + +/* pp.c */ +void pp_add_option (const char *opt, const char *arg); +char *pp_command_line (void); + +char *mkfilename (const char *dir, const char *name, const char *suf); + +union pies_sockaddr_storage +{ + struct sockaddr s; + struct sockaddr_in s_in; + struct sockaddr_un s_un; +}; + +/* addrfmt.c */ +void sockaddr_to_str (const struct sockaddr *sa, int salen, + char *bufptr, size_t buflen, + size_t *plen); +char *sockaddr_to_astr (const struct sockaddr *sa, int salen); + + diff --git a/lib/mkfilename.c b/lib/mkfilename.c new file mode 100644 index 0000000..8841fe4 --- /dev/null +++ b/lib/mkfilename.c @@ -0,0 +1,42 @@ +/* This file is part of GNU Pies + Copyright (C) 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "libpies.h" +#include <grecs.h> + +char * +mkfilename (const char *dir, const char *name, const char *suf) +{ + size_t dirlen = strlen (dir); + char *s; + + while (dirlen > 0 && dir[dirlen-1] == '/') + dirlen--; + + s = malloc (dirlen + 1 + strlen (name) + (suf ? strlen (suf) : 0) + 1); + if (!s) + grecs_alloc_die (); + strcpy (s, dir); + strcat (s, "/"); + strcat (s, name); + if (suf) + strcat (s, suf); + return s; +} + diff --git a/lib/netrc.c b/lib/netrc.c new file mode 100644 index 0000000..ed8053f --- /dev/null +++ b/lib/netrc.c @@ -0,0 +1,217 @@ +/* This file is part of GNU Pies + Copyright (C) 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "libpies.h" +#include <unistd.h> +#include <pwd.h> +#include <grecs.h> +#include <wordsplit.h> +#include <errno.h> +#include <netdb.h> + +/* Compare two hostnames. Return 0 if they have the same address type, + address length *and* at least one of the addresses of A matches + B */ +static int +hostcmp (const char *a, const char *b) +{ + struct hostent *hp = gethostbyname (a); + char **addrlist; + char *dptr; + char **addr; + size_t i, count; + size_t entry_length; + int entry_type; + + if (!hp) + return 1; + + for (count = 1, addr = hp->h_addr_list; *addr; addr++) + count++; + addrlist = grecs_malloc (count * (sizeof *addrlist + hp->h_length) + - hp->h_length); + dptr = (char *) (addrlist + count); + for (i = 0; i < count - 1; i++) + { + memcpy (dptr, hp->h_addr_list[i], hp->h_length); + addrlist[i] = dptr; + dptr += hp->h_length; + } + addrlist[i] = NULL; + entry_length = hp->h_length; + entry_type = hp->h_addrtype; + + hp = gethostbyname (b); + if (!hp || entry_length != hp->h_length || entry_type != hp->h_addrtype) + { + grecs_free (addrlist); + return 1; + } + + for (addr = addrlist; *addr; addr++) + { + char **p; + + for (p = hp->h_addr_list; *p; p++) + { + if (memcmp (*addr, *p, entry_length) == 0) + { + grecs_free (addrlist); + return 0; + } + } + } + grecs_free (addrlist); + return 1; +} + +static int +match_url (size_t argc, char **argv, struct pies_url *url) +{ + if (hostcmp (argv[1], url->host ? url->host : "localhost") == 0) + { + if (argc >= 4 && strcmp (argv[2], "port") == 0) + { + unsigned long n = strtoul (argv[3], NULL, 10); + if (n == url->port) + return 1; + } + else + return 1; + } + return 0; +} + +static void +parse_args (struct grecs_locus *loc, char **argv, + char **username, char **password) +{ + if (*username) + { + grecs_free (*username); + *username = NULL; + } + if (*password) + { + grecs_free (*password); + *password = NULL; + } + + while (*argv) + { + if (!argv[1]) + { + grecs_error (loc, 0, _("incomplete sentence")); + break; + } + if (strcmp (*argv, "login") == 0) + *username = grecs_strdup (argv[1]); + else if (strcmp (*argv, "password") == 0) + *password = grecs_strdup (argv[1]); + argv += 2; + } +} + +/* Parse traditional .netrc file. Set up auth_args fields in accordance with + it. */ +void +netrc_scan_file (FILE *fp, struct grecs_locus *loc, struct pies_url *url) +{ + char *buf = NULL; + size_t n = 0; + struct wordsplit ws; + int wsflags = WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS; + char *username = NULL; + char *password = NULL; + + while (grecs_getline (&buf, &n, fp) > 0) + { + loc->beg.line++; + + if (wordsplit (buf, &ws, wsflags)) + { + grecs_error (loc, 0, "wordsplit: %s", wordsplit_strerror (&ws)); + continue; + } + wsflags |= WRDSF_REUSE; + + if (ws.ws_wordc == 0) + continue; + + if (strcmp (ws.ws_wordv[0], "machine") == 0) + { + if (match_url (ws.ws_wordc, ws.ws_wordv, url)) + { + parse_args (loc, ws.ws_wordv + 2, &username, &password); + break; + } + } + else if (strcmp (ws.ws_wordv[0], "default") == 0) + parse_args (loc, ws.ws_wordv + 1, &username, &password); + else + grecs_error (loc, 0, _("ignoring unrecognized line\n")); + } + grecs_free (buf); + + if (wsflags & WRDSF_REUSE) + wordsplit_free (&ws); + + url->user = username; + url->passwd = password; +} + +void +netrc_scan (struct pies_url *url) +{ + FILE *fp; + struct grecs_locus loc; + char *filename; + char *homedir; + + if (url->user) + return; + + homedir = getenv ("HOME"); + if (!homedir) + { + struct passwd *pwd = getpwuid (getuid ()); + if (!pwd) + return; + homedir = pwd->pw_dir; + } + + filename = mkfilename (homedir, ".netrc", NULL); + fp = fopen (filename, "r"); + if (!fp) + { + if (errno != ENOENT) + grecs_error (NULL, 0, + _("cannot open configuration file %s: %s"), + filename, strerror (errno)); + free (filename); + return; + } + + loc.beg.file = loc.end.file = (char*) filename; + loc.beg.col = loc.end.col = 0; + loc.beg.line = 0; + netrc_scan_file (fp, &loc, url); + fclose (fp); + free (filename); +} diff --git a/lib/pp.c b/lib/pp.c new file mode 100644 index 0000000..9960d7f --- /dev/null +++ b/lib/pp.c @@ -0,0 +1,71 @@ +/* This file is part of GNU Pies. + Copyright (C) 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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <grecs.h> +#include <sysexits.h> + +struct grecs_txtacc *pp_cmd; + +void +pp_add_option (const char *opt, const char *arg) +{ + if (!DEFAULT_PREPROCESSOR) + { + grecs_error (NULL, 0, _("no preprocessor configured")); + exit (EX_CONFIG); + } + if (!pp_cmd) + { + pp_cmd = grecs_txtacc_create (); + grecs_txtacc_grow_string (pp_cmd, DEFAULT_PREPROCESSOR); + } + grecs_txtacc_grow_char (pp_cmd, ' '); + grecs_txtacc_grow_string (pp_cmd, opt); + + if (arg) + { + grecs_txtacc_grow_char (pp_cmd, '\''); + for (; *arg; ++arg) + { + if (*arg == '\'') + grecs_txtacc_grow_string (pp_cmd, "'\\''"); + else + grecs_txtacc_grow (pp_cmd, arg, 1); + } + grecs_txtacc_grow_char (pp_cmd, '\''); + } +} + +char * +pp_command_line (void) +{ + char *ret; + + if (!DEFAULT_PREPROCESSOR) + return NULL; + + if (!pp_cmd) + { + pp_cmd = grecs_txtacc_create (); + grecs_txtacc_grow_string (pp_cmd, DEFAULT_PREPROCESSOR); + } + grecs_txtacc_grow_char (pp_cmd, 0); + ret = grecs_txtacc_finish (pp_cmd, 1); + grecs_txtacc_free (pp_cmd); + pp_cmd = NULL; + return ret; +} diff --git a/lib/split3.c b/lib/split3.c new file mode 100644 index 0000000..ee302e8 --- /dev/null +++ b/lib/split3.c @@ -0,0 +1,83 @@ +/* This file is part of GNU Pies. + Copyright (C) 2007, 2008, 2009, 2010, 2013 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "libpies.h" + +static int +split3 (const char *input, char *res[3], int flag) +{ + size_t len; + char *p; + + p = strchr (input, ' '); + if (!p) + return 1; + + len = p - input; + res[0] = malloc (len + 1); + if (!res[0]) + return -1; + memcpy (res[0], input, len); + res[0][len] = 0; + + input = p + 1; + + p = (flag ? strrchr : strchr) (input, ' '); + + if (!p) + return 1; + + len = p - input; + res[1] = malloc (len + 1); + if (!res[1]) + return -1; + memcpy (res[1], input, len); + res[1][len] = 0; + + res[2] = strdup (p + 1); + if (!res[2]) + return -1; + + return 0; +} + +int +strsplit3 (const char *input, char *result[3], int flag) +{ + char *tmp[3] = { NULL, NULL, NULL }; + int rc = split3 (input, tmp, flag); + if (rc) + { + int ec = errno; + free (tmp[0]); + free (tmp[1]); + free (tmp[2]); + errno = ec; + } + else + { + result[0] = tmp[0]; + result[1] = tmp[1]; + result[2] = tmp[2]; + } + return 0; +} diff --git a/lib/url.c b/lib/url.c new file mode 100644 index 0000000..41f5c04 --- /dev/null +++ b/lib/url.c @@ -0,0 +1,360 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <netdb.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include "libpies.h" +#include <wordsplit.h> + +/* 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)); < |