summaryrefslogtreecommitdiff
path: root/libmailutils/url/create.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/url/create.c')
-rw-r--r--libmailutils/url/create.c525
1 files changed, 525 insertions, 0 deletions
diff --git a/libmailutils/url/create.c b/libmailutils/url/create.c
new file mode 100644
index 000000000..674a52469
--- /dev/null
+++ b/libmailutils/url/create.c
@@ -0,0 +1,525 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <limits.h>
+
+#include <mailutils/util.h>
+#include <mailutils/errno.h>
+#include <mailutils/argcv.h>
+#include <mailutils/secret.h>
+#include <mailutils/cstr.h>
+#include <mailutils/sys/url.h>
+
+struct mu_url_ctx
+{
+ int flags;
+ const char *input;
+ const char *cur;
+ mu_url_t url;
+
+ size_t passoff;
+
+ char *tokbuf;
+ size_t toksize;
+ size_t toklen;
+};
+
+static int
+getkn (struct mu_url_ctx *ctx, char *delim)
+{
+ size_t n;
+
+ if (*ctx->cur == 0)
+ return -1;
+ n = strcspn (ctx->cur, delim);
+ if (n > ctx->toksize)
+ {
+ char *p = realloc (ctx->tokbuf, n + 1);
+ if (!p)
+ return ENOENT;
+ ctx->toksize = n + 1;
+ ctx->tokbuf = p;
+ }
+ memcpy (ctx->tokbuf, ctx->cur, n);
+ ctx->tokbuf[n] = 0;
+ ctx->toklen = n;
+ ctx->cur += n;
+ return 0;
+}
+
+#define INIT_ARRAY_SIZE 16
+
+static int
+expand_array (size_t *pwc, char ***pwv, int incr)
+{
+ size_t wc = *pwc;
+ char **wv = *pwv;
+
+ if (!wv)
+ {
+ wv = calloc (INIT_ARRAY_SIZE, sizeof (wv[0]));
+ wc = INIT_ARRAY_SIZE;
+ }
+ else
+ {
+ if (incr)
+ wc += incr;
+ else
+ {
+ size_t newsize = wc * 2;
+ if (newsize < wc)
+ return ENOMEM;
+ wc = newsize;
+ }
+ wv = realloc (wv, sizeof (wv[0]) * wc);
+ }
+ if (!wv)
+ return ENOMEM;
+ *pwv = wv;
+ *pwc = wc;
+ return 0;
+}
+
+static int
+parse_param (struct mu_url_ctx *ctx, char *delim, int *pargc, char ***pargv)
+{
+ int rc;
+ size_t wc = 0, wn = 0;
+ char **wv = NULL;
+
+ while ((rc = getkn (ctx, delim)) == 0)
+ {
+ if (wn == wc)
+ {
+ rc = expand_array (&wc, &wv, 0);
+ if (rc)
+ break;
+ }
+ wv[wn] = strdup (ctx->tokbuf);
+ if (!wv[wn])
+ {
+ rc = ENOMEM;
+ break;
+ }
+ wn++;
+ if (*ctx->cur != delim[0])
+ break;
+ ctx->cur++;
+ }
+
+ if (rc == 0)
+ {
+ if (wn == wc)
+ {
+ rc = expand_array (&wc, &wv, 1);
+ if (rc)
+ {
+ mu_argcv_free (wc, wv);
+ return ENOMEM;
+ }
+ wv[wn] = NULL;
+ }
+
+ *pargv = realloc (wv, sizeof (wv[0]) * (wn + 1));
+ *pargc = wn;
+ }
+ else
+ mu_argcv_free (wc, wv);
+
+ return rc;
+}
+
+static int
+_mu_url_ctx_parse_query (struct mu_url_ctx *ctx)
+{
+ int rc;
+
+ ctx->cur++;
+ rc = parse_param (ctx, "&", &ctx->url->qargc, &ctx->url->qargv);
+ if (rc == 0 && ctx->url->qargc)
+ ctx->url->flags |= MU_URL_QUERY;
+ return rc;
+}
+
+static int
+_mu_url_ctx_parse_param (struct mu_url_ctx *ctx)
+{
+ int rc;
+
+ ctx->cur++;
+ rc = parse_param (ctx, ";?", &ctx->url->fvcount, &ctx->url->fvpairs);
+ if (rc)
+ return rc;
+ if (ctx->url->fvcount)
+ ctx->url->flags |= MU_URL_PARAM;
+ if (*ctx->cur == '?')
+ return _mu_url_ctx_parse_query (ctx);
+ return 0;
+}
+
+static int
+str_assign (char **ptr, const char *str)
+{
+ *ptr = strdup (str);
+ if (!*ptr)
+ return ENOMEM;
+ return 0;
+}
+
+static int
+_mu_url_ctx_parse_path (struct mu_url_ctx *ctx)
+{
+ int rc;
+ mu_url_t url = ctx->url;
+
+ rc = getkn (ctx, ";?");
+ if (rc)
+ return rc;
+ rc = str_assign (&url->path, ctx->tokbuf);
+ if (rc == 0)
+ url->flags |= MU_URL_PATH;
+ if (*ctx->cur == ';')
+ return _mu_url_ctx_parse_param (ctx);
+ if (*ctx->cur == '?')
+ return _mu_url_ctx_parse_query (ctx);
+ return 0;
+}
+
+static int
+_mu_url_ctx_parse_host (struct mu_url_ctx *ctx, int has_host)
+{
+ int rc;
+ mu_url_t url = ctx->url;
+
+ rc = getkn (ctx, ":/;?");
+ if (rc)
+ return rc;
+
+ if (ctx->toklen)
+ {
+ rc = str_assign (&url->host, ctx->tokbuf);
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_HOST;
+ has_host = 1;
+ }
+
+ if (*ctx->cur == ':')
+ {
+ ctx->cur++;
+ has_host = 1;
+
+ rc = getkn (ctx, "/;?");
+ if (rc)
+ return rc;
+
+ rc = str_assign (&url->portstr, ctx->tokbuf);
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_PORT;
+ }
+
+ if (*ctx->cur == '/')
+ {
+ if (has_host)
+ ctx->cur++;
+ return _mu_url_ctx_parse_path (ctx);
+ }
+
+ if (*ctx->cur == ';')
+ return _mu_url_ctx_parse_param (ctx);
+
+ if (*ctx->cur == '?')
+ return _mu_url_ctx_parse_query (ctx);
+ return 0;
+}
+
+static int
+_mu_url_ctx_parse_cred (struct mu_url_ctx *ctx)
+{
+ int rc, has_cred;
+ mu_url_t url = ctx->url;
+ const char *save = ctx->cur;
+
+ rc = getkn (ctx, "@");
+ if (rc)
+ return rc;
+ has_cred = *ctx->cur == '@';
+ /* restore the pointer */
+ ctx->cur = save;
+ if (has_cred)
+ {
+ /* Try to split the user into a:
+ <user>:<password>
+ or
+ <user>:<password>;AUTH=<auth>
+ */
+ rc = getkn (ctx, ":;@");
+ if (rc)
+ return rc;
+
+ if (ctx->toklen)
+ {
+ rc = str_assign (&url->user, ctx->tokbuf);
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_USER;
+ }
+
+ if (*ctx->cur == ':')
+ {
+ ctx->cur++;
+ ctx->passoff = ctx->cur - ctx->input;
+
+ rc = getkn (ctx, ";@");
+ if (rc)
+ return rc;
+
+ if (ctx->toklen)
+ {
+ if (mu_secret_create (&url->secret, ctx->tokbuf, ctx->toklen))
+ return ENOMEM;
+ else
+ /* Clear password */
+ memset (ctx->tokbuf, 0, ctx->toklen);
+ url->flags |= MU_URL_SECRET;
+ }
+ }
+ if (*ctx->cur == ';')
+ {
+ ctx->cur++;
+
+ rc = getkn (ctx, "@");
+ if (rc)
+ return rc;
+
+ /* Make sure it's the auth token. */
+ if (mu_c_strncasecmp (ctx->tokbuf, "auth=", 5) == 0)
+ {
+ rc = str_assign (&url->auth, ctx->tokbuf + 5);
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_AUTH;
+ }
+ }
+
+ /* Skip @ sign */
+ ctx->cur++;
+ }
+ return _mu_url_ctx_parse_host (ctx, has_cred);
+}
+
+int
+_mu_url_ctx_parse (struct mu_url_ctx *ctx)
+{
+ int rc;
+ mu_url_t url = ctx->url;
+
+ /* Parse the scheme part */
+ rc = getkn (ctx, ":/");
+ if (rc)
+ return rc;
+ if (*ctx->cur == ':')
+ {
+ rc = str_assign (&url->scheme, ctx->tokbuf);
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_SCHEME;
+ ctx->cur++;
+ }
+
+ if (*ctx->cur == 0)
+ return 0;
+
+ if (ctx->cur[0] == '/' && ctx->cur[1] == '/')
+ {
+ ctx->cur += 2;
+ return _mu_url_ctx_parse_cred (ctx);
+ }
+
+ return _mu_url_ctx_parse_path (ctx);
+}
+
+static int
+_mu_url_create_internal (struct mu_url_ctx *ctx, mu_url_t hint)
+{
+ int rc;
+ mu_url_t url = ctx->url;
+
+ if ((ctx->flags & MU_URL_PARSE_PIPE) && ctx->input[0] == '|')
+ {
+ rc = str_assign (&url->scheme, "prog");
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_SCHEME;
+ ctx->flags &= ~MU_URL_PARSE_HEXCODE;
+ rc = mu_argcv_get (ctx->input + 1, NULL, NULL, &url->qargc, &url->qargv);
+ if (rc == 0)
+ {
+ url->flags |= MU_URL_QUERY;
+ rc = str_assign (&url->path, url->qargv[0]);
+ if (rc == 0)
+ url->flags |= MU_URL_PATH;
+ }
+ }
+ else if ((ctx->flags & MU_URL_PARSE_SLASH) && ctx->input[0] == '/')
+ {
+ rc = str_assign (&url->scheme, "file");
+ if (rc)
+ return rc;
+ url->flags |= MU_URL_SCHEME;
+ ctx->flags &= ~MU_URL_PARSE_HEXCODE;
+ rc = str_assign (&url->path, ctx->input);
+ if (rc == 0)
+ url->flags |= MU_URL_PATH;
+ }
+ else
+ rc = _mu_url_ctx_parse (ctx);
+
+ if (rc)
+ return rc;
+
+ if (hint)
+ {
+ /* Fill in missing values */
+ rc = mu_url_copy_hints (url, hint);
+ if (rc)
+ return rc;
+ }
+
+ if (!(url->flags & MU_URL_SCHEME))
+ return MU_ERR_URL_MISS_PARTS;
+
+ /* RFC 1738, section 2.1, lower the scheme case */
+ mu_strlower (url->scheme);
+
+ if ((url->flags & MU_URL_PORT) && url->port == 0)
+ {
+ /* Convert port string to number */
+ unsigned long n;
+ char *p;
+
+ n = strtoul (url->portstr, &p, 10);
+ if (*p)
+ {
+ if (ctx->flags & MU_URL_PARSE_PORTSRV)
+ {
+ /* FIXME: Another proto? */
+ struct servent *sp = getservbyname (url->portstr, "tcp");
+ if (!sp)
+ return MU_ERR_TCP_NO_PORT; //FIXME: Error code?
+ url->port = ntohs (sp->s_port);
+ }
+ else
+ return MU_ERR_TCP_NO_PORT;
+ }
+ else if (n > USHRT_MAX)
+ return ERANGE;
+ else
+ url->port = n;
+ }
+
+ if (ctx->flags & MU_URL_PARSE_HEXCODE)
+ {
+ /* Decode the %XX notations */
+ rc = mu_url_decode (url);
+ if (rc)
+ return rc;
+ }
+
+ if ((url->flags & MU_URL_SECRET) &&
+ (ctx->flags & MU_URL_PARSE_HIDEPASS))
+ {
+ /* Obfuscate the password */
+#define PASS_REPL "***"
+#define PASS_REPL_LEN (sizeof (PASS_REPL) - 1)
+ size_t plen = mu_secret_length (url->secret);
+ size_t nlen = strlen (url->name);
+ size_t len = nlen - plen + PASS_REPL_LEN + 1;
+ char *newname;
+
+ memset (url->name + ctx->passoff, 0, plen);
+ if (len > nlen + 1)
+ {
+ newname = realloc (url->name, len);
+ if (!newname)
+ return rc;
+ url->name = newname;
+ }
+ else
+ newname = url->name;
+ memmove (newname + ctx->passoff + PASS_REPL_LEN,
+ newname + ctx->passoff + plen,
+ nlen - (ctx->passoff + plen) + 1);
+ memcpy (newname + ctx->passoff, PASS_REPL, PASS_REPL_LEN);
+ }
+
+ return 0;
+}
+
+int
+mu_url_create_hint (mu_url_t *purl, const char *str, int flags,
+ mu_url_t hint)
+{
+ int rc;
+ struct mu_url_ctx ctx;
+ mu_url_t url = calloc (1, sizeof (*url));
+ if (url == NULL)
+ return ENOMEM;
+ url->name = strdup (str);
+ if (!url->name)
+ {
+ free (url);
+ return ENOMEM;
+ }
+ memset (&ctx, 0, sizeof (ctx));
+ ctx.flags = flags;
+ ctx.input = str;
+ ctx.cur = ctx.input;
+ ctx.url = url;
+ rc = _mu_url_create_internal (&ctx, hint);
+ free (ctx.tokbuf);
+ if (rc)
+ mu_url_destroy (&url);
+ else
+ *purl = url;
+ return rc;
+}
+
+int
+mu_url_create (mu_url_t *purl, const char *str)
+{
+ return mu_url_create_hint (purl, str,
+ MU_URL_PARSE_HEXCODE |
+ MU_URL_PARSE_HIDEPASS |
+ MU_URL_PARSE_PORTSRV |
+ MU_URL_PARSE_PIPE |
+ MU_URL_PARSE_SLASH, NULL);
+}

Return to:

Send suggestions and report system problems to the System administrator.