From 0f915b110e0ac0810871a80da8ac08bcdcfff37e Mon Sep 17 00:00:00 2001 From: "Steffen (Daode) Nurpmeso" Date: Fri, 30 May 2014 23:46:51 +0200 Subject: [PATCH] Add struct url and url_parse() --- auxlily.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ catd/en_US | 3 + nail.1 | 11 +++ nail.h | 33 +++++++++ nailfuns.h | 5 ++ 5 files changed, 294 insertions(+) diff --git a/auxlily.c b/auxlily.c index f60e167e..964f70b2 100644 --- a/auxlily.c +++ b/auxlily.c @@ -765,6 +765,248 @@ nodename(int mayoverride) return hostname; } +FL bool_t +url_parse(struct url *urlp, enum cproto cproto, char const *data) +{ +#if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP +# define __ALLPROTO +#endif +#if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP +# define __ANYPROTO + char *cp, *x; +#endif + bool_t rv = FAL0; + NYD_ENTER; + UNUSED(data); + + memset(urlp, 0, sizeof *urlp); + urlp->url_input = data; + urlp->url_cproto = cproto; + + /* Network protocol */ +#define _protox(X,Y) \ + urlp->url_portno = Y;\ + memcpy(urlp->url_proto, X "://", sizeof(X "://"));\ + urlp->url_proto[sizeof(X) -1] = '\0';\ + urlp->url_proto_len = sizeof(X) -1;\ + urlp->url_proto_xlen = sizeof(X "://") -1 +#define __if(X,Y,Z) \ + if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\ + _protox(X, Y);\ + data += sizeof(X "://") -1;\ + do { Z; } while (0);\ + goto juser;\ + } +#define _if(X,Y) __if(X, Y, (void)0) +#ifdef HAVE_SSL +# define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1) +#else +# define _ifs(X,Y) goto jeproto; +#endif + + switch (cproto) { + case CPROTO_SMTP: +#ifdef HAVE_SMTP + _if ("smtp", 25) + _if ("submission", 587) + _ifs ("smtps", 465) + _protox("smtp", 25); + break; +#else + goto jeproto; +#endif + case CPROTO_POP3: +#ifdef HAVE_POP3 + _if ("pop3", 110) + _ifs ("pop3s", 995) + _protox("pop3", 110); + break; +#else + goto jeproto; +#endif + case CPROTO_IMAP: +#ifdef HAVE_IMAP + _if ("imap", 143) + _ifs ("imaps", 993) + _protox("imap", 143); + break; +#else + goto jeproto; +#endif + } + +#undef _ifs +#undef _if +#undef __if +#undef _protox + + if (strstr(data, "://") != NULL) { +#if !defined __ALLPROTO || !defined HAVE_SSL +jeproto: +#endif + fprintf(stderr, tr(574, "URL `proto://' prefix invalid: `%s'\n"), + urlp->url_input); + goto jleave; + } +#ifdef __ANYPROTO + + /* User and password, I */ +juser: + if ((cp = UNCONST(last_at_before_slash(data))) == NULL) + goto jserver; + + urlp->url_had_user = TRU1; + urlp->url_user.s = savestrbuf(data, urlp->url_user.l = PTR2SIZE(cp - data)); + data = ++cp; + + /* And also have a password? */ + if ((cp = strchr(urlp->url_user.s, ':')) != NULL) { + x = urlp->url_user.s; + urlp->url_user.s = savestrbuf(x, urlp->url_user.l = PTR2SIZE(cp - x)); + urlp->url_pass.l = strlen(urlp->url_pass.s = urlxdec(++cp)); + urlp->url_pass_enc.l = strlen( + urlp->url_pass_enc.s = urlxenc(urlp->url_pass.s)); + } + + /* Servername and port -- and possible path suffix */ +jserver: + if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, IPv6 support */ + char *eptr; + long l; + + urlp->url_port = x = savestr(x = cp + 1); + if ((x = strchr(x, '/')) != NULL) + *x = '\0'; + l = strtol(urlp->url_port, &eptr, 10); + if (*eptr != '\0' || l <= 0 || UICMP(32, l, >=, 0xFFFFu)) { + fprintf(stderr, tr(573, "URL with invalid port number: `%s'\n"), + urlp->url_input); + goto jleave; + } + urlp->url_portno = (ui16_t)l; + } else { + if ((x = strchr(data, '/')) != NULL) + data = savestrbuf(data, PTR2SIZE(x - data)); + cp = UNCONST(data + strlen(data)); + } + + /* A (non-empty) path may only occur with IMAP */ + if (x != NULL && x[1] != '\0') { + if (cproto != CPROTO_IMAP) { + fprintf(stderr, tr(575, "URL protocol doesn't support paths: `%s'\n"), + urlp->url_input); + goto jleave; + } + urlp->url_path.l = strlen(++x); + urlp->url_path.s = savestrbuf(x, urlp->url_path.l); + } + + urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data)); + { size_t i; + for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i) + *cp = lowerconv(*cp); + } + + /* HOST:PORT */ + { size_t i; + struct str *s = &urlp->url_hp; + + s->s = salloc(urlp->url_host.l + 1 + sizeof("65536")-1 +1); + memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l); + if (urlp->url_port != NULL) { + size_t j = strlen(urlp->url_port); + s->s[i++] = ':'; + memcpy(s->s + i, urlp->url_port, j); + i += j; + } + s->s[i] = '\0'; + s->l = i; + } + + /* User, II + * If there was no user in the URL, do we have *user-HOST* or *user*? */ + if (!urlp->url_had_user) { + char const usrstr[] = "user-"; + size_t const usrlen = sizeof(usrstr) -1; + + cp = ac_alloc(usrlen + urlp->url_hp.l +1); + memcpy(cp, usrstr, usrlen); + memcpy(cp + usrlen, urlp->url_hp.s, urlp->url_hp.l +1); + if ((urlp->url_user.s = vok_vlook(cp)) == NULL) { + cp[usrlen - 1] = '\0'; + if ((urlp->url_user.s = vok_vlook(cp)) == NULL) + urlp->url_user.s = UNCONST(myname); + } + ac_free(cp); + } + + urlp->url_user.l = strlen(urlp->url_user.s = urlxdec(urlp->url_user.s)); + urlp->url_user_enc.l = strlen( + urlp->url_user_enc.s = urlxenc(urlp->url_user.s)); + + /* USER@HOST:PORT */ + if (urlp->url_user_enc.l == 0) + urlp->url_uhp = urlp->url_hp; + else { + struct str *s = &urlp->url_uhp; + size_t i = urlp->url_user_enc.l; + + s->s = salloc(i + 1 + urlp->url_hp.l +1); + if (i > 0) { + memcpy(s->s, urlp->url_user_enc.s, i); + s->s[i++] = '@'; + } + memcpy(s->s + i, urlp->url_hp.s, urlp->url_hp.l +1); + i += urlp->url_hp.l; + s->l = i; + } + + /* USER@HOST */ + if (urlp->url_user_enc.l == 0) + urlp->url_uh = urlp->url_host; + else { + struct str *s = &urlp->url_uh; + size_t i = urlp->url_user_enc.l; + + s->s = salloc(i + 1 + urlp->url_host.l +1); + if (i > 0) { + memcpy(s->s, urlp->url_user_enc.s, i); + s->s[i++] = '@'; + } + memcpy(s->s + i, urlp->url_host.s, urlp->url_host.l +1); + i += urlp->url_host.l; + s->l = i; + } + + /* Finally, for fun: .url_proto://.url_uhp[/.url_path] */ + { size_t i; + char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_uhp.l) + + 1 + urlp->url_path.l +1); + + urlp->url_proto[urlp->url_proto_len] = ':'; + memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_uhp.s, urlp->url_uhp.l +1); + urlp->url_proto[urlp->url_proto_len] = '\0'; + + if (urlp->url_path.l == 0) + urlp->url_puhp = urlp->url_puhpp = ud; + else { + urlp->url_puhp = savestrbuf(ud, i); + urlp->url_puhpp = ud; + ud += i; + *ud++ = '/'; + memcpy(ud, urlp->url_path.s, urlp->url_path.l +1); + } + } + + rv = TRU1; +#endif /* __ANYPROTO */ +jleave: + NYD_LEAVE; + return rv; +#undef __ANYPROTO +#undef __ALLPROTO +} + FL char * lookup_password_for_token(char const *token) { diff --git a/catd/en_US b/catd/en_US index 7a9d43dc..94f228f6 100644 --- a/catd/en_US +++ b/catd/en_US @@ -572,3 +572,6 @@ $ It follows the "help command" one line help block, alpha-sorted (see nail.1) 570 The value of *attrlist* is not of the correct length\n 571 %s `%s' is not defined\n 572 *sendcharsets* and *charset-8bit* iteration exhausted, restarting\n +573 URL with invalid port number: `%s'\n +574 URL `proto://' prefix invalid: `%s'\n +575 URL protocol doesn't support paths: `%s'\n diff --git a/nail.1 b/nail.1 index e7980935..29e6faae 100644 --- a/nail.1 +++ b/nail.1 @@ -3789,6 +3789,17 @@ from the `LC_CTYPE' locale environment. Refer to the section .Sx "Character sets" for the complete picture about character sets. +.It Va user +\*(IN Sets a global fallback user name, which is used in case none has +been given in the protocol and account-specific URL and there is also +no matching +.Va user-HOST . +This variable defaults to the value of +.Va USER . +.It Va user-HOST +\*(IN Overrides +.Va user +for a specific host. .It Va VISUAL Pathname of the text editor to use in the .Ic visual diff --git a/nail.h b/nail.h index b7a5ecc5..5639411f 100644 --- a/nail.h +++ b/nail.h @@ -669,6 +669,12 @@ enum protocol { PROTO_UNKNOWN /* unknown protocol */ }; +enum cproto { + CPROTO_SMTP, + CPROTO_POP3, + CPROTO_IMAP +}; + #ifdef HAVE_SSL enum ssl_verify_level { SSL_VERIFY_IGNORE, @@ -888,6 +894,33 @@ struct colour_table { struct str ct_csinfo[COLOURSPEC_RESET+1 + 1]; }; +struct url { + char const *url_input; /* Input as given (really) */ + enum cproto url_cproto; /* Communication protocol as given */ + bool_t url_needs_tls; /* Wether the protocol uses SSL/TLS */ + bool_t url_had_user; /* Wether .url_user was part of the URL */ + ui16_t url_portno; /* atoi .url_port or default, host endian */ + char const *url_port; /* Port (if given) or NULL */ + char url_proto[14]; /* Communication protocol as 'xy\0//' */ + ui8_t url_proto_len; /* Length of .url_proto ('\0' index) */ + ui8_t url_proto_xlen; /* .. if '\0' is replaced with ':' */ + struct str url_user; /* User, urlxdec()oded */ + struct str url_user_enc; /* User, urlxenc()oded */ + struct str url_pass; /* Pass (urlxdec()oded) or NULL */ + struct str url_pass_enc; /* Pass (urlxenc()oded) or NULL */ + struct str url_host; /* Service hostname */ + struct str url_path; /* CPROTO_IMAP: path suffix or NULL */ + /* TODO: url_get_component(url *, enum COMPONENT, str *store) */ + struct str url_hp; /* .url_host[:.url_port] */ + struct str url_uhp; /* .url_user_enc@.url_host[:.url_port] */ + /* .url_user_enc@.url_host + * Note: for CPROTO_SMTP this may resolve HOST via *smtp-hostname* (-> + * *hostname*)! (And may later be overwritten according to *from*!) */ + struct str url_uh; + char const *url_puhp; /* .url_proto://.url_uhp */ + char const *url_puhpp; /* .url_proto://.url_uhp[/.url_path] */ +}; + struct time_current { time_t tc_time; struct tm tc_gm; diff --git a/nailfuns.h b/nailfuns.h index f21b426f..832f9a72 100644 --- a/nailfuns.h +++ b/nailfuns.h @@ -303,6 +303,11 @@ FL char * getprompt(void); /* Detect and query the hostname to use */ FL char * nodename(int mayoverride); +/* Parse data, which must meet the criteria of the protocol cproto, and fill + * in the URL structure urlp */ +FL bool_t url_parse(struct url *urlp, enum cproto cproto, + char const *data); + /* Try to lookup a variable named "password-*token*". * Return NULL or salloc()ed buffer */ FL char * lookup_password_for_token(char const *token); -- 2.11.4.GIT