2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-3.10-20241025

This commit is contained in:
Wietse Z Venema 2024-10-25 00:00:00 -05:00 committed by Viktor Dukhovni
parent c7a9de81bf
commit a559c5da49
10 changed files with 346 additions and 102 deletions

View File

@ -28439,3 +28439,11 @@ Apologies for any names omitted.
Documentation: in a pgsql: client configuration, the setting Documentation: in a pgsql: client configuration, the setting
"dbname" is required, but ignored when the setting "hosts" "dbname" is required, but ignored when the setting "hosts"
contains an URI with a database name. File: proto/pgsql_table. contains an URI with a database name. File: proto/pgsql_table.
20241025
Cleanup: accept any well-formed URI prefix as a pgsql: client
connection target (the PostgreSQL URI parser decides what
is allowed). The dbname setting is now optional if the hosts
setting specifies only URIs. Files: util/valid_uri_scheme.[hc],
proto/pgsql_table.

View File

@ -43,22 +43,30 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
<b><a name="pgsql_parameters">PGSQL PARAMETERS</a></b> <b><a name="pgsql_parameters">PGSQL PARAMETERS</a></b>
<b>hosts</b> The hosts that Postfix will try to connect to and query from. <b>hosts</b> The hosts that Postfix will try to connect to and query from.
Besides a <b>postgresql://</b> connection URI, this setting supports Besides a PostgreSQL connection URI, this setting supports the
the historical forms <b>unix:/</b><i>pathname</i> for UNIX-domain sockets and historical forms <b>unix:/</b><i>pathname</i> for UNIX-domain sockets and
<b>inet:</b><i>host:port</i> for TCP connections, where the <b>unix:</b> and <b>inet:</b> <b>inet:</b><i>host:port</i> for TCP connections, where the <b>unix:</b> and <b>inet:</b>
prefixes are accepted and ignored for backwards compatibility. prefixes are accepted and ignored for backwards compatibility.
Examples: Examples:
hosts = postgresql://username@example.com/<i>databasename</i>?sslmode=require hosts = postgresql://username@example.com/<i>databasename</i>?sslmode=require
hosts = postgres://user:secret@localhost
hosts = inet:host1.some.domain inet:host2.some.domain:port hosts = inet:host1.some.domain inet:host2.some.domain:port
hosts = host1.some.domain host2.some.domain:port hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name hosts = unix:/file/name
See <a href="https://www.postgresql.org/docs/current/libpq-connect.html">https://www.postgresql.org/docs/current/libpq-connect.html</a>
for the supported connection URI syntax.
The hosts are tried in random order. The connections are auto- The hosts are tried in random order. The connections are auto-
matically closed after being idle for about 1 minute, and are matically closed after being idle for about 1 minute, and are
re-opened as necessary. re-opened as necessary. See <b>idle_interval</b> for details.
NOTE: if "hosts" specifies one load balancer and no alternative NOTE: if <b>hosts</b> specifies a PostgreSQL connection URI, the Post-
servers, specify the load balancer multiple times in the "hosts" greSQL client library will ignore the <b>dbname</b> setting for that
connection.
NOTE: if <b>hosts</b> specifies one load balancer and no alternative
servers, specify the load balancer multiple times in the <b>hosts</b>
line. Without the duplicate info, the Postfix PostgreSQL client line. Without the duplicate info, the Postfix PostgreSQL client
would not reconnect immediately to the same load balancer after would not reconnect immediately to the same load balancer after
a PostgreSQL server failure. a PostgreSQL server failure.
@ -71,12 +79,15 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
user = someone user = someone
password = some_password password = some_password
<b>dbname</b> (required) <b>dbname</b> The database name on the servers. Example:
The database name on the servers. Example:
dbname = customer_database dbname = customer_database
This setting is required, but ignored when a postgresql:// URI The <b>dbname</b> setting is ignored for <b>hosts</b> connections that are
specifies a database name. specified as an URI.
The <b>dbname</b> setting is required with Postfix 3.10 and later, when
<b>hosts</b> specifies any non-URI connection; it is always required
with earlier Postfix versions.
<b>encoding</b> <b>encoding</b>
The encoding used by the database client. The default setting The encoding used by the database client. The default setting

View File

@ -55,7 +55,7 @@ return the key itself or a constant value.
.fi .fi
.IP "\fBhosts\fR" .IP "\fBhosts\fR"
The hosts that Postfix will try to connect to and query The hosts that Postfix will try to connect to and query
from. Besides a \fBpostgresql://\fR connection URI, this from. Besides a PostgreSQL connection URI, this
setting supports the historical forms \fBunix:/\fIpathname\fR setting supports the historical forms \fBunix:/\fIpathname\fR
for UNIX\-domain sockets and \fBinet:\fIhost:port\fR for TCP for UNIX\-domain sockets and \fBinet:\fIhost:port\fR for TCP
connections, where the \fBunix:\fR and \fBinet:\fR prefixes connections, where the \fBunix:\fR and \fBinet:\fR prefixes
@ -63,18 +63,28 @@ are accepted and ignored for backwards compatibility.
Examples: Examples:
.nf .nf
hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require
hosts = postgres://user:secret@localhost
hosts = inet:host1.some.domain inet:host2.some.domain:port hosts = inet:host1.some.domain inet:host2.some.domain:port
hosts = host1.some.domain host2.some.domain:port hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name hosts = unix:/file/name
.fi .fi
See https://www.postgresql.org/docs/current/libpq\-connect.html
for the supported connection URI syntax.
The hosts are tried in random order. The connections are The hosts are tried in random order. The connections are
automatically closed after being idle for about 1 minute, automatically closed after being idle for about 1 minute,
and are re\-opened as necessary. and are re\-opened as necessary. See \fBidle_interval\fR
for details.
NOTE: if "hosts" specifies one load balancer and no alternative NOTE: if \fBhosts\fR specifies a PostgreSQL connection URI,
the PostgreSQL client library will ignore the \fBdbname\fR
setting for that connection.
NOTE: if \fBhosts\fR specifies one load balancer and no
alternative
servers, specify the load balancer multiple times in the servers, specify the load balancer multiple times in the
"hosts" line. Without the duplicate info, the Postfix \fBhosts\fR line. Without the duplicate info, the Postfix
PostgreSQL client would not reconnect immediately to the PostgreSQL client would not reconnect immediately to the
same load balancer after a PostgreSQL server failure. same load balancer after a PostgreSQL server failure.
.IP "\fBuser\fR" .IP "\fBuser\fR"
@ -85,14 +95,18 @@ Example:
user = someone user = someone
password = some_password password = some_password
.fi .fi
.IP "\fBdbname\fR (required)" .IP "\fBdbname\fR"
The database name on the servers. Example: The database name on the servers. Example:
.nf .nf
dbname = customer_database dbname = customer_database
.fi .fi
.sp .sp
This setting is required, but ignored when a postgresql:// The \fBdbname\fR setting is ignored for \fBhosts\fR connections
URI specifies a database name. that are specified as an URI.
The \fBdbname\fR setting is required with Postfix 3.10 and later,
when \fBhosts\fR specifies any non\-URI connection; it is always
required with earlier Postfix versions.
.IP "\fBencoding\fR" .IP "\fBencoding\fR"
The encoding used by the database client. The default setting The encoding used by the database client. The default setting
is: is:

View File

@ -45,7 +45,7 @@
# .fi # .fi
# .IP "\fBhosts\fR" # .IP "\fBhosts\fR"
# The hosts that Postfix will try to connect to and query # The hosts that Postfix will try to connect to and query
# from. Besides a \fBpostgresql://\fR connection URI, this # from. Besides a PostgreSQL connection URI, this
# setting supports the historical forms \fBunix:/\fIpathname\fR # setting supports the historical forms \fBunix:/\fIpathname\fR
# for UNIX-domain sockets and \fBinet:\fIhost:port\fR for TCP # for UNIX-domain sockets and \fBinet:\fIhost:port\fR for TCP
# connections, where the \fBunix:\fR and \fBinet:\fR prefixes # connections, where the \fBunix:\fR and \fBinet:\fR prefixes
@ -53,18 +53,28 @@
# Examples: # Examples:
# .nf # .nf
# hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require # hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require
# hosts = postgres://user:secret@localhost
# hosts = inet:host1.some.domain inet:host2.some.domain:port # hosts = inet:host1.some.domain inet:host2.some.domain:port
# hosts = host1.some.domain host2.some.domain:port # hosts = host1.some.domain host2.some.domain:port
# hosts = unix:/file/name # hosts = unix:/file/name
# .fi # .fi
# #
# See https://www.postgresql.org/docs/current/libpq-connect.html
# for the supported connection URI syntax.
#
# The hosts are tried in random order. The connections are # The hosts are tried in random order. The connections are
# automatically closed after being idle for about 1 minute, # automatically closed after being idle for about 1 minute,
# and are re-opened as necessary. # and are re-opened as necessary. See \fBidle_interval\fR
# for details.
# #
# NOTE: if "hosts" specifies one load balancer and no alternative # NOTE: if \fBhosts\fR specifies a PostgreSQL connection URI,
# the PostgreSQL client library will ignore the \fBdbname\fR
# setting for that connection.
#
# NOTE: if \fBhosts\fR specifies one load balancer and no
# alternative
# servers, specify the load balancer multiple times in the # servers, specify the load balancer multiple times in the
# "hosts" line. Without the duplicate info, the Postfix # \fBhosts\fR line. Without the duplicate info, the Postfix
# PostgreSQL client would not reconnect immediately to the # PostgreSQL client would not reconnect immediately to the
# same load balancer after a PostgreSQL server failure. # same load balancer after a PostgreSQL server failure.
# .IP "\fBuser\fR" # .IP "\fBuser\fR"
@ -75,14 +85,18 @@
# user = someone # user = someone
# password = some_password # password = some_password
# .fi # .fi
# .IP "\fBdbname\fR (required)" # .IP "\fBdbname\fR"
# The database name on the servers. Example: # The database name on the servers. Example:
# .nf # .nf
# dbname = customer_database # dbname = customer_database
# .fi # .fi
# .sp # .sp
# This setting is required, but ignored when a postgresql:// # The \fBdbname\fR setting is ignored for \fBhosts\fR connections
# URI specifies a database name. # that are specified as an URI.
#
# The \fBdbname\fR setting is required with Postfix 3.10 and later,
# when \fBhosts\fR specifies any non-URI connection; it is always
# required with earlier Postfix versions.
# .IP "\fBencoding\fR" # .IP "\fBencoding\fR"
# The encoding used by the database client. The default setting # The encoding used by the database client. The default setting
# is: # is:

View File

@ -1252,6 +1252,7 @@ dict_pgsql.o: ../../include/myrand.h
dict_pgsql.o: ../../include/split_at.h dict_pgsql.o: ../../include/split_at.h
dict_pgsql.o: ../../include/stringops.h dict_pgsql.o: ../../include/stringops.h
dict_pgsql.o: ../../include/sys_defs.h dict_pgsql.o: ../../include/sys_defs.h
dict_pgsql.o: ../../include/valid_uri_scheme.h
dict_pgsql.o: ../../include/vbuf.h dict_pgsql.o: ../../include/vbuf.h
dict_pgsql.o: ../../include/vstream.h dict_pgsql.o: ../../include/vstream.h
dict_pgsql.o: ../../include/vstring.h dict_pgsql.o: ../../include/vstring.h

View File

@ -92,6 +92,7 @@
#include "myrand.h" #include "myrand.h"
#include "events.h" #include "events.h"
#include "stringops.h" #include "stringops.h"
#include "valid_uri_scheme.h"
/* Global library. */ /* Global library. */
@ -127,6 +128,7 @@ typedef struct {
typedef struct { typedef struct {
int len_hosts; /* number of hosts */ int len_hosts; /* number of hosts */
HOST **db_hosts; /* hosts on which databases reside */ HOST **db_hosts; /* hosts on which databases reside */
char *non_uri_target; /* require dbname to be specified */
} PLPGSQL; } PLPGSQL;
typedef struct { typedef struct {
@ -626,7 +628,7 @@ static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0); dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0); dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0); dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 0, 0);
dict_pgsql->encoding = cfg_get_str(p, "encoding", "UTF8", 1, 0); dict_pgsql->encoding = cfg_get_str(p, "encoding", "UTF8", 1, 0);
dict_pgsql->retry_interval = cfg_get_int(p, "retry_interval", dict_pgsql->retry_interval = cfg_get_int(p, "retry_interval",
DEF_RETRY_INTV, 1, 0); DEF_RETRY_INTV, 1, 0);
@ -723,6 +725,21 @@ DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts); dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
if (dict_pgsql->pldb == NULL) if (dict_pgsql->pldb == NULL)
msg_fatal("couldn't initialize pldb!\n"); msg_fatal("couldn't initialize pldb!\n");
if (msg_verbose && dict_pgsql->pldb->non_uri_target == 0
&& dict_pgsql->dbname[0] != 0)
msg_info("%s:%s table ignores 'dbname' field -- "
"all 'hosts' targets are URIs",
DICT_TYPE_PGSQL, name);
if (dict_pgsql->pldb->non_uri_target && dict_pgsql->dbname[0] == 0) {
DICT *ret;
ret == (dict_surrogate(DICT_TYPE_PGSQL, name, open_flags, dict_flags,
"%s:%s host target '%s' requires dbname setting",
DICT_TYPE_PGSQL, name,
dict_pgsql->pldb->non_uri_target));
dict_pgsql_close(&dict_pgsql->dict);
return (ret);
}
dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser); dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser);
return (DICT_DEBUG (&dict_pgsql->dict)); return (DICT_DEBUG (&dict_pgsql->dict));
} }
@ -737,8 +754,12 @@ static PLPGSQL *plpgsql_init(ARGV *hosts)
PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL)); PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL));
PLDB->len_hosts = hosts->argc; PLDB->len_hosts = hosts->argc;
PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc); PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc);
for (i = 0; i < hosts->argc; i++) PLDB->non_uri_target = 0;
for (i = 0; i < hosts->argc; i++) {
PLDB->db_hosts[i] = host_init(hosts->argv[i]); PLDB->db_hosts[i] = host_init(hosts->argv[i]);
if (PLDB->db_hosts[i]->type != TYPECONNSTR)
PLDB->non_uri_target = PLDB->db_hosts[i]->name;
}
return PLDB; return PLDB;
} }
@ -758,9 +779,9 @@ static HOST *host_init(const char *hostname)
host->ts = 0; host->ts = 0;
/* /*
* Modern syntax: "postgresql://connection-info". * Modern syntax: connection URI.
*/ */
if (strncmp(d, "postgresql:", 11) == 0) { if (valid_uri_scheme(d)) {
host->type = TYPECONNSTR; host->type = TYPECONNSTR;
host->name = mystrdup(d); host->name = mystrdup(d);
host->port = 0; host->port = 0;

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20241024" #define MAIL_RELEASE_DATE "20241025"
#define MAIL_VERSION_NUMBER "3.10" #define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@ -46,7 +46,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \ sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \ mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
inet_addr_sizes.c quote_for_json.c mystrerror.c \ inet_addr_sizes.c quote_for_json.c mystrerror.c \
sane_sockaddr_to_hostaddr.c normalize_ws.c sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@ -94,7 +94,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \ sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \ mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \ quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
normalize_ws.o normalize_ws.o valid_uri_scheme.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros. # otherwise it sets the PLUGIN_* macros.
@ -126,7 +126,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \ valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \
check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \ check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \
known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \ known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \
inet_prefix_top.h inet_addr_sizes.h inet_prefix_top.h inet_addr_sizes.h valid_uri_scheme.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \ TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE) DEFS = -I. -D$(SYSTYPE)
@ -149,7 +149,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
vbuf_print split_qnameval vstream msg_logger byte_mask \ vbuf_print split_qnameval vstream msg_logger byte_mask \
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \ known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
clean_env inet_prefix_top printable readlline quote_for_json \ clean_env inet_prefix_top printable readlline quote_for_json \
normalize_ws normalize_ws valid_uri_scheme
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \ PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX) $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
HTABLE_FIX = NORANDOMIZE=1 HTABLE_FIX = NORANDOMIZE=1
@ -618,6 +618,11 @@ normalize_ws: $(LIB)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o mv junk $@.o
valid_uri_scheme: $(LIB)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
tests: all valid_hostname_test mac_expand_test dict_test unescape_test \ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test host_port_test dict_tests \ attr_scan64_test attr_scan0_test host_port_test dict_tests \
@ -629,7 +634,7 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \ vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
binhash_test argv_test inet_prefix_top_test printable_test \ binhash_test argv_test inet_prefix_top_test printable_test \
valid_utf8_string_test readlline_test quote_for_json_test \ valid_utf8_string_test readlline_test quote_for_json_test \
normalize_ws_test normalize_ws_test valid_uri_scheme_test
dict_tests: all dict_test \ dict_tests: all dict_test \
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \ dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
@ -1109,6 +1114,9 @@ quote_for_json_test: quote_for_json
normalize_ws_test: normalize_ws normalize_ws_test: normalize_ws
$(SHLIB_ENV) ${VALGRIND} ./normalize_ws $(SHLIB_ENV) ${VALGRIND} ./normalize_ws
valid_uri_scheme_test: valid_uri_scheme
$(SHLIB_ENV) ${VALGRIND} ./valid_uri_scheme
depend: $(MAKES) depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \ (sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \ set -e; for i in [a-z][a-z0-9]*.c; do \
@ -2893,6 +2901,16 @@ valid_hostname.o: valid_hostname.c
valid_hostname.o: valid_hostname.h valid_hostname.o: valid_hostname.h
valid_hostname.o: vbuf.h valid_hostname.o: vbuf.h
valid_hostname.o: vstring.h valid_hostname.o: vstring.h
valid_uri_scheme.o: check_arg.h
valid_uri_scheme.o: msg.h
valid_uri_scheme.o: msg_vstream.h
valid_uri_scheme.o: stringops.h
valid_uri_scheme.o: sys_defs.h
valid_uri_scheme.o: valid_uri_scheme.c
valid_uri_scheme.o: valid_uri_scheme.h
valid_uri_scheme.o: vbuf.h
valid_uri_scheme.o: vstream.h
valid_uri_scheme.o: vstring.h
valid_utf8_hostname.o: check_arg.h valid_utf8_hostname.o: check_arg.h
valid_utf8_hostname.o: midna_domain.h valid_utf8_hostname.o: midna_domain.h
valid_utf8_hostname.o: msg.h valid_utf8_hostname.o: msg.h

View File

@ -0,0 +1,129 @@
/*++
/* NAME
/* valid_uri_scheme 3
/* SUMMARY
/* validate scheme:// prefix
/* SYNOPSIS
/* #include <valid_uri_scheme.h>
/*
/* int valid_uri_scheme(const char *str)
/* DESCRIPTION
/* valid_uri_scheme() takes a null-terminated string and returns
/* the length of a valid scheme:// prefix, or zero if no valid
/* prefix was found.
/*
/* This function requires that input is encoded in ASCII or UTF-8.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
#include <ctype.h>
#include <stdlib.h>
/*
* Utility library.
*/
#include <valid_uri_scheme.h>
#include <msg.h>
#include <msg_vstream.h>
#include <stringops.h>
/* valid_uri_scheme - predicate that string starts with scheme:// */
ssize_t valid_uri_scheme(const char *str)
{
const char *cp = str;
int ch = *cp++;
/* Per RFC 3986, a valid scheme starts with ALPHA. */
if (!ISALPHA(ch))
return (0);
while ((ch = *cp++) != 0) {
/* A valid scheme continues with ALPHA | DIGIT | '+' | '-'. */
if (ISALNUM(ch) || ch == '+' || ch == '-')
continue;
/* A valid scheme is followed by "://". */
if (ch == ':' && *cp++ == '/' && *cp++ == '/')
return (cp - str);
/* Not a valid scheme. */
break;
}
/* Not a valid scheme. */
return (0);
}
#ifdef TEST
typedef struct TEST_CASE {
const char *label;
const char *input;
const ssize_t want;
} TEST_CASE;
#define PASS (0)
#define FAIL (1)
static const TEST_CASE test_cases[] = {
{"accepts_alpha_scheme", "abcd://blah", sizeof("abcd://") - 1},
{"accepts_mixed_scheme", "a-bcd+123://blah", sizeof("a-bcd+123://") - 1},
{"rejects_minus_first", "-bcd+123://blah'", 0},
{"rejects_plus_first", "+123://blah", 0},
{"rejects_digit_first", "123://blah", 0},
{"rejects_other_first", "?123://blah", 0},
{"rejects_other_middle", "abcd?123://blah", 0},
{"rejects_other_end", "abcd-123?://blah", 0},
{"rejects_non_scheme", "inet:host:port", 0},
{"rejects_no_colon", "inet", 0},
{"rejects_colon_slash", "abcd:/blah", 0},
{"rejects_empty", "", 0},
{0,}
};
static int test_validate_scheme(const TEST_CASE *tp)
{
int got;
got = valid_uri_scheme(tp->input);
if (got != tp->want) {
msg_warn("got '%ld', want '%ld'", (long) got, (long) tp->want);
return (FAIL);
}
return (PASS);
}
int main(int argc, char **argv)
{
const TEST_CASE *tp;
int pass = 0;
int fail = 0;
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
for (tp = test_cases; tp->label != 0; tp++) {
int test_failed;
msg_info("RUN %s", tp->label);
test_failed = test_validate_scheme(tp);
if (test_failed) {
msg_info("FAIL %s", tp->label);
fail++;
} else {
msg_info("PASS %s", tp->label);
pass++;
}
}
msg_info("PASS=%d FAIL=%d", pass, fail);
exit(fail != 0);
}
#endif

View File

@ -0,0 +1,28 @@
#ifndef _VALID_SCHEME_H_INCLUDED_
#define _VALID_SCHEME_H_INCLUDED_
/*++
/* NAME
/* valid_uri_scheme 3h
/* SUMMARY
/* validate scheme:// prefix
/* SYNOPSIS
/* #include <valid_uri_scheme.h>
/* DESCRIPTION
/* .nf
/*
* External interface.
*/
extern ssize_t valid_uri_scheme(const char *);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
#endif