>From b02cea3ead352a198f341c0f2a9f6ab93f439077 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 18 Sep 2014 17:06:21 +0900
Subject: [PATCH 2/2] Make pg_hba.conf case insensitive.

---
 src/backend/libpq/hba.c |  101 ++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 83 insertions(+), 18 deletions(-)

diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 84da823..e4b1635 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -60,9 +60,18 @@ typedef struct check_network_data
 	bool		result;			/* set to true if match */
 } check_network_data;
 
+typedef enum TokenType
+{
+	NORMAL,
+	GROUP_NAME,			/* this token had leading '+' */
+	FILE_INCLUSION,		/* this token had leading '@' */
+} TokenType;
 
-#define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
-#define token_matches(t, k)  (strcmp(t->string, k) == 0)
+#define token_is_keyword(tk, kw)	\
+	((tk)->type == NORMAL && !(tk)->case_preserved &&	\
+	 (strcmp((tk)->string, (kw)) == 0))
+#define token_matches(t, k)	\
+	((t)->type == NORMAL && (strcmp((t)->string, (k)) == 0))
 
 /*
  * A single string token lexed from the HBA config file, together with whether
@@ -71,7 +80,8 @@ typedef struct check_network_data
 typedef struct HbaToken
 {
 	char	   *string;
-	bool		quoted;
+	TokenType	type;
+	bool		case_preserved;
 } HbaToken;
 
 /*
@@ -123,8 +133,14 @@ pg_isblank(const char c)
  * the first character.  (We use that to prevent "@x" from being treated
  * as a file inclusion request.  Note that @"x" should be so treated;
  * we want to allow that to support embedded spaces in file paths.)
+ * type is one of NORMAL, GROUP_NAME, FILE_INCLUSION. GROUP_NAME is set if the
+ * token is prefix by '+', FILE_INCLUSION if prefixed by '@', NORMAL
+ * otherwise.
  * We set *terminating_comma to indicate whether the token is terminated by a
  * comma (which is not returned.)
+ * case_preserved is set if the token's case of every character is preserved
+ * when the whole word is enclosed by double quotes. Elsewise returned token
+ * string is lowercased.
  *
  * If successful: store null-terminated token at *buf and return TRUE.
  * If no more tokens on line: set *buf = '\0' and return FALSE.
@@ -136,8 +152,8 @@ pg_isblank(const char c)
  * Handle comments.
  */
 static bool
-next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
-		   bool *terminating_comma)
+next_token(char **lineptr, char *buf, int bufsz,
+		   bool *case_preserved, int *type, bool *terminating_comma)
 {
 	int			c;
 	char	   *start_buf = buf;
@@ -149,8 +165,9 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 	/* end_buf reserves two bytes to ensure we can append \n and \0 */
 	Assert(end_buf > start_buf);
 
-	*initial_quote = false;
 	*terminating_comma = false;
+	*case_preserved = false;
+	*type = NORMAL;
 
 	/* Move over initial whitespace and commas */
 	while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
@@ -162,6 +179,17 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 		return false;
 	}
 
+	if (c == '+' || c == '@')
+	{
+		*type = (c == '+' ? GROUP_NAME : FILE_INCLUSION);
+
+		/*
+		 * Skip capturing it, and we can read the following characters as
+		 * usual.
+		 */
+		c = *(*lineptr)++;
+	}
+
 	/*
 	 * Build a token in buf of next characters up to EOF, EOL, unquoted comma,
 	 * or unquoted whitespace.
@@ -201,8 +229,17 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 		}
 
 		if (c != '"' || was_quote)
+		{
 			*buf++ = c;
 
+			/*
+			 * Cancel case-sensitive state if trailing characters found for
+			 * the quoted region.
+			 */
+			if (*case_preserved && !in_quote)
+				*case_preserved = false;
+		}
+
 		/* Literal double-quote is two double-quotes */
 		if (in_quote && c == '"')
 			was_quote = !was_quote;
@@ -214,7 +251,7 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 			in_quote = !in_quote;
 			saw_quote = true;
 			if (buf == start_buf)
-				*initial_quote = true;
+				*case_preserved = true;
 		}
 
 		c = *(*lineptr)++;
@@ -226,13 +263,32 @@ next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote,
 	 */
 	(*lineptr)--;
 
+	/*
+	 * only '@' alone is treated as the character itself for backward
+	 * compatibility.
+	 */
+	if (buf == start_buf && *type == FILE_INCLUSION)
+	{
+		*buf++ = '@';
+		*type = NORMAL;
+	}
+
 	*buf = '\0';
 
-	return (saw_quote || buf > start_buf);
+	/* Down case the names if non-case-preserve notation */
+	if (!*case_preserved)
+	{
+		char *p;
+		for (p = start_buf ; p < buf ; p++) 
+			*p = tolower(*p);
+	}
+
+	/* Allowing empty group name for backward comptibility */
+	return (saw_quote || buf > start_buf || *type == GROUP_NAME);
 }
 
 static HbaToken *
-make_hba_token(char *token, bool quoted)
+make_hba_token(char *token, TokenType toktype, bool case_preserved)
 {
 	HbaToken   *hbatoken;
 	int			toklen;
@@ -240,7 +296,8 @@ make_hba_token(char *token, bool quoted)
 	toklen = strlen(token);
 	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
 	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
-	hbatoken->quoted = quoted;
+	hbatoken->type = toktype;
+	hbatoken->case_preserved = case_preserved;
 	memcpy(hbatoken->string, token, toklen + 1);
 
 	return hbatoken;
@@ -252,7 +309,8 @@ make_hba_token(char *token, bool quoted)
 static HbaToken *
 copy_hba_token(HbaToken *in)
 {
-	HbaToken   *out = make_hba_token(in->string, in->quoted);
+	HbaToken   *out = make_hba_token(in->string,
+									 in->type, in->case_preserved);
 
 	return out;
 }
@@ -269,19 +327,26 @@ next_field_expand(const char *filename, char **lineptr)
 {
 	char		buf[MAX_TOKEN];
 	bool		trailing_comma;
-	bool		initial_quote;
+	bool		case_preserved;
+	int			type;
 	List	   *tokens = NIL;
 
 	do
 	{
-		if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma))
+		if (!next_token(lineptr, buf, sizeof(buf), 
+						&case_preserved, &type,
+						&trailing_comma))
 			break;
 
 		/* Is this referencing a file? */
-		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
-			tokens = tokenize_inc_file(tokens, filename, buf + 1);
+		if (type == FILE_INCLUSION)
+			tokens = tokenize_inc_file(tokens, filename, buf);
+		else if (type == GROUP_NAME)
+			tokens = lappend(tokens,
+							 make_hba_token(buf, GROUP_NAME, case_preserved));
 		else
-			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
+			tokens = lappend(tokens,
+							 make_hba_token(buf, NORMAL, case_preserved));
 	} while (trailing_comma);
 
 	return tokens;
@@ -489,9 +554,9 @@ check_role(const char *role, Oid roleid, List *tokens)
 	foreach(cell, tokens)
 	{
 		tok = lfirst(cell);
-		if (!tok->quoted && tok->string[0] == '+')
+		if (tok->type == GROUP_NAME)
 		{
-			if (is_member(roleid, tok->string + 1))
+			if (is_member(roleid, tok->string))
 				return true;
 		}
 		else if (token_matches(tok, role) ||
-- 
1.7.1

