summaryrefslogtreecommitdiff
path: root/libmailutils/url/create.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2010-10-26 13:00:52 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2010-10-26 13:36:28 +0300
commit3327a23a49e532c068972a0b2d64021361540f7b (patch)
treeb2d6508005ac8343a54638142c4f8234f7202441 /libmailutils/url/create.c
parent6203ae65f53758a4b893e6e176be7c153a31bef0 (diff)
downloadmailutils-3327a23a49e532c068972a0b2d64021361540f7b.tar.gz
mailutils-3327a23a49e532c068972a0b2d64021361540f7b.tar.bz2
Rewrite URL support.
The purpose is to make it modular and flexible. URLs are parsed out as they are created. Missing URL parts can be supplied via a "URL hint" at creation time (similar to the approach used in creating mu_address_t). Ports can be specified either as numbers or as service names. Original port string representation can be retrieved from the URL, as well as its numeric value. * libmailutils/url/accessor.h: New file. * libmailutils/url/copy.c: New file. * libmailutils/url/create.c: New file. * libmailutils/url/decode.c: New file. * libmailutils/url/destroy.c: New file. * libmailutils/url/dup.c: New file. * libmailutils/url/expand.c: New file. * libmailutils/url/flag.c: New file. * libmailutils/url/get-auth.c: New file. * libmailutils/url/get-host.c: New file. * libmailutils/url/get-param.c: New file. * libmailutils/url/get-path.c: New file. * libmailutils/url/get-portstr.c: New file. * libmailutils/url/get-query.c: New file. * libmailutils/url/get-scheme.c: New file. * libmailutils/url/get-secret.c: New file. * libmailutils/url/get-user.c: New file. * libmailutils/url/match.c: New file. * libmailutils/url/port.c: New file. * libmailutils/url/scheme.c: New file. * libmailutils/url/uplevel.c: New file. * libmailutils/url/urlstr.c: New file. * configure.ac (AC_CONFIG_FILES): Add libmailutils/url/Makefile * libmailutils/Makefile.am (SUBDIRS): Add url. (libmailutils_la_LIBADD): Link with liburl. * libmailutils/base/Makefile.am (libbase_la_SOURCES): Remove url.c * libmailutils/base/url.c: Remove. * libmailutils/string/Makefile.am (libstring_la_SOURCES): Add xdecode.c * libmailutils/string/xdecode.c: New file. * include/mailutils/sys/url.h (_mu_url): Change type to short. <_get_port>: Change second argument to unsigned. <_get_portstr>: New method. * include/mailutils/url.h (MU_URL_SCHEME): New flag. (MU_URL_PARSE_HEXCODE, MU_URL_PARSE_HIDEPASS) (MU_URL_PARSE_PORTSRV, MU_URL_PARSE_PORTWC) (MU_URL_PARSE_PIPE, MU_URL_PARSE_SLASH): New flags. (mu_url_create_hint, mu_url_copy_hints): New prototypes. (mu_url_parse): Remove. (mu_url_get_port): Change second argument to unsigned. (mu_url_decode_len,mu_url_decode): Remove. (mu_url_decode): New proto. (mu_url_sget_portstr, mu_url_aget_portstr) (mu_url_get_portstr): New protos. * include/mailutils/util.h (mu_str_url_decode) (mu_str_url_decode_inline): New protos. * libproto/pop/mbox.c (pop_open): Port is unsigned. * libproto/imap/folder.c: Use MU_URL_SCHEME in url_may_have. * libproto/maildir/folder.c: Likewise. * libproto/mailer/prog.c: Likewise. * libproto/mailer/remote.c: Likewise. * libproto/mailer/sendmail.c: Likewise. * libproto/mailer/smtp.c: Likewise. * libproto/mbox/folder.c: Likewise. * libproto/mh/folder.c: Likewise. * libproto/nntp/folder.c: Likewise. * libproto/pop/folder.c: Likewise. * imap4d/imap4d.c: Remove calls to mu_url_parse. * libmailutils/base/registrar.c: Likewise. * libmailutils/base/wicket.c: Likewise. * libmailutils/mailbox/folder.c: Likewise. * libmailutils/mailbox/mailbox.c: Likewise. * libmailutils/mailer/mailer.c: Likewise. * libmailutils/tests/url-parse.c: Likewise. * libmailutils/tests/wicket.c: Likewise. * libproto/mailer/smtp_auth.c: Likewise. * maidag/deliver.c: Likewise. * mu/wicket.c: Likewise. * libmailutils/mime/mimehdr.c (mu_mimehdr_decode_param): Use mu_str_url_decode, instead of mu_url_decode. * libmailutils/stream/tcp.c (_tcp_instance)<port>: Change type to unsigned short. All uses updated. (mu_tcp_stream_create_with_source_ip) (mu_tcp_stream_create_with_source_host) (mu_tcp_stream_create): Port is unsigned. * include/mailutils/stream.h (mu_tcp_stream_create_with_source_ip) (mu_tcp_stream_create_with_source_host) (mu_tcp_stream_create): Port is unsigned. * include/mailutils/cpp/url.h (get_port): Return unsigned. * libmu_cpp/url.cc (get_port): Return unsigned. (parse): Empty function. Schedule for removal. * python/libmu_py/url.c (api_url_parse): Empty function. Schedule for removal. (api_url_get_port): Port is unsigned. * libmailutils/base/wicket.c (mu_wicket_file_match_url) (mu_wicket_file_match_url): New parameter: parse_flags. * mu/wicket.c (wicket_match): Use parse_flags to control whether or not to show the plaintext password. * doc/texinfo/url.texi: Update.
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.