From: Marco Nenciarini <mnencia@debian.org>
Date: Sat, 10 Aug 2013 16:26:41 +0200
Subject: encoding

---
 mod_auth_pgsql.c    | 144 +++++++++++++++++++++++++++++++---------------------
 mod_auth_pgsql.html |   9 ++++
 2 files changed, 94 insertions(+), 59 deletions(-)

diff --git a/mod_auth_pgsql.c b/mod_auth_pgsql.c
index f13c166..639537d 100644
--- a/mod_auth_pgsql.c
+++ b/mod_auth_pgsql.c
@@ -151,6 +151,7 @@ typedef struct {
 	const char *auth_pg_port;
 	const char *auth_pg_options;
 	const char *auth_pg_user;
+	const char *auth_pg_charset;
 	const char *auth_pg_pwd;
 	const char *auth_pg_pwd_table;
 	const char *auth_pg_uname_field;
@@ -181,6 +182,8 @@ typedef struct {
 
 } pg_auth_config_rec;
 
+static PGconn *pg_conn;
+
 static apr_pool_t *auth_pgsql_pool = NULL;
 static apr_pool_t *auth_pgsql_pool_base64 = NULL;
 
@@ -220,6 +223,7 @@ static void *create_pg_auth_dir_config(apr_pool_t * p, char *d)
 	new_rec->auth_pg_port = NULL;
 	new_rec->auth_pg_options = NULL;
 	new_rec->auth_pg_user = NULL;
+	new_rec->auth_pg_charset = NULL;
 	new_rec->auth_pg_pwd = NULL;
 	new_rec->auth_pg_pwd_table = NULL;
 	new_rec->auth_pg_uname_field = NULL;
@@ -324,6 +328,10 @@ static const command_rec pg_auth_cmds[] = {
 				  (void *) APR_OFFSETOF(pg_auth_config_rec, auth_pg_user),
 				  OR_AUTHCFG,
 				  "user name connect as"),
+	AP_INIT_TAKE1("Auth_PG_charset", ap_set_string_slot,
+				  (void *) APR_OFFSETOF(pg_auth_config_rec, auth_pg_charset),
+				  OR_AUTHCFG,
+				  "charset to use for connection"),
 	AP_INIT_TAKE1("Auth_PG_pwd", ap_set_string_slot,
 				  (void *) APR_OFFSETOF(pg_auth_config_rec, auth_pg_pwd),
 				  OR_AUTHCFG,
@@ -462,53 +470,51 @@ static char *auth_pg_base64(char *pw)
 }
 
 
+PGconn *pg_connect(pg_auth_config_rec *sec)
+{
+	PGconn *conn;
 
-/* Got from POstgreSQL 7.2 */
-/* ---------------
- * Escaping arbitrary strings to get valid SQL strings/identifiers.
- *
- * Replaces "\\" with "\\\\" and "'" with "''".
- * length is the length of the buffer pointed to by
- * from.  The buffer at to must be at least 2*length + 1 characters
- * long.  A terminating NUL character is written.
- * ---------------
- */
+	conn = PQsetdbLogin(sec->auth_pg_host, sec->auth_pg_port,
+		sec->auth_pg_options, NULL, sec->auth_pg_database,
+		sec->auth_pg_user, sec->auth_pg_pwd);
+	if (PQstatus(conn) != CONNECTION_OK) {
+		PQreset(conn);
+		apr_snprintf(pg_errstr, MAX_STRING_LEN,
+					 "mod_auth_pgsql database connection error resetting %s",
+					 PQerrorMessage(conn));
+		if (PQstatus(conn) != CONNECTION_OK) {
+			apr_snprintf(pg_errstr, MAX_STRING_LEN,
+						 "mod_auth_pgsql database connection error reset failed %s",
+						 PQerrorMessage(conn));
+			PQfinish(conn);
+			return NULL;
+		}
+	}
+	return conn;
+}
 
-static size_t pg_check_string(char *to, const char *from, size_t length)
-{
-	const char *source = from;
-	char *target = to;
-	unsigned int remaining = length;
-
-	while (remaining > 0) {
-		switch (*source) {
-		case '\\':
-			*target = '\\';
-			target++;
-			*target = '\\';
-			/* target and remaining are updated below. */
-			break;
 
-		case '\'':
-			*target = '\'';
-			target++;
-			*target = '\'';
-			/* target and remaining are updated below. */
-			break;
+static size_t pg_check_string(char *to, const char *from, size_t length, request_rec * r, pg_auth_config_rec *sec)
+{
+	int error;
 
-		default:
-			*target = *source;
-			/* target and remaining are updated below. */
+	if (!pg_conn) {
+		if (!(pg_conn = pg_connect(sec))) {
+			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "[mod_auth_pgsql.c] - cannot connect to database");
+			ap_note_basic_auth_failure(r);
+			return -1;
 		}
-		source++;
-		target++;
-		remaining--;
 	}
 
-	/* Write the terminating NUL character. */
-	*target = '\0';
+	PQescapeStringConn(pg_conn, to, from, length, &error);
+
+	if (error) {
+		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "[mod_auth_pgsql.c] - cannot escape string");
+		ap_note_basic_auth_failure(r);
+		return -1;
+	}
 
-	return target - to;
+	return 0;
 }
 
 
@@ -518,7 +524,6 @@ static size_t pg_check_string(char *to, const char *from, size_t length)
 char *do_pg_query(request_rec * r, char *query, pg_auth_config_rec * sec)
 {
 	PGresult *pg_result;
-	PGconn *pg_conn;
 	char *val;
 	char *result = NULL;
 
@@ -530,19 +535,10 @@ char *do_pg_query(request_rec * r, char *query, pg_auth_config_rec * sec)
 	  sec->auth_pg_database);
 #endif							/* DEBUG_AUTH_PGSQL */
 
-	pg_conn = PQsetdbLogin(sec->auth_pg_host, sec->auth_pg_port,
-		sec->auth_pg_options, NULL, sec->auth_pg_database,
-		sec->auth_pg_user, sec->auth_pg_pwd);
-	if (PQstatus(pg_conn) != CONNECTION_OK) {
-		PQreset(pg_conn);
-		apr_snprintf(pg_errstr, MAX_STRING_LEN,
-					 "mod_auth_pgsql database connection error resetting %s",
-					 PQerrorMessage(pg_conn));
-		if (PQstatus(pg_conn) != CONNECTION_OK) {
-			apr_snprintf(pg_errstr, MAX_STRING_LEN,
-						 "mod_auth_pgsql database connection error reset failed %s",
-						 PQerrorMessage(pg_conn));
-			PQfinish(pg_conn);
+	if (!pg_conn) {
+		if (!(pg_conn = pg_connect(sec))) {
+			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "[mod_auth_pgsql.c] - cannot connect to database");
+			ap_note_basic_auth_failure(r);
 			return NULL;
 		}
 	}
@@ -552,6 +548,21 @@ char *do_pg_query(request_rec * r, char *query, pg_auth_config_rec * sec)
 				  query);
 #endif							/* DEBUG_AUTH_PGSQL */
 
+	if (sec->auth_pg_charset) {
+		const char *check;
+
+		PQsetClientEncoding(pg_conn, sec->auth_pg_charset);
+
+		check = pg_encoding_to_char(PQclientEncoding(pg_conn));
+
+		if (!check || strcmp(sec->auth_pg_charset, check)) {
+			apr_snprintf(pg_errstr, MAX_STRING_LEN,
+						 "mod_auth_pgsql database character set encoding %s");
+			PQfinish(pg_conn);
+			return NULL;
+		}
+	}
+
 	pg_result = PQexec(pg_conn, query);
 
 	if (pg_result == NULL) {
@@ -610,7 +621,7 @@ char *get_pg_pw(request_rec * r, char *user, pg_auth_config_rec * sec)
 	int n;
 
 	safe_user = apr_palloc(r->pool, 1 + 2 * strlen(user));
-	pg_check_string(safe_user, user, strlen(user));
+	pg_check_string(safe_user, user, strlen(user), r, sec);
 
 #ifdef DEBUG_AUTH_PGSQL
 	ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
@@ -685,8 +696,8 @@ static char *get_pg_grp(request_rec * r, char *group, char *user,
 #endif							/* DEBUG_AUTH_PGSQL */
 
 	query[0] = '\0';
-	pg_check_string(safe_user, user, strlen(user));
-	pg_check_string(safe_group, group, strlen(group));
+	pg_check_string(safe_user, user, strlen(user), r, sec);
+	pg_check_string(safe_group, group, strlen(group), r, sec);
 
 	if ((!sec->auth_pg_grp_table) ||
 		(!sec->auth_pg_grp_group_field) || (!sec->auth_pg_grp_user_field))
@@ -777,6 +788,14 @@ static int pg_authenticate_basic_user(request_rec * r)
 	}
 	pg_errstr[0] = '\0';
 
+	if (!pg_conn) {
+		if (!(pg_conn = pg_connect(sec))) {
+			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "[mod_auth_pgsql.c] - cannot connect to database");
+			ap_note_basic_auth_failure(r);
+			return HTTP_UNAUTHORIZED;
+		}
+	}
+
 	if (sec->auth_pg_cache_passwords
 		&& (!apr_is_empty_table(sec->cache_pass_table))) {
 		val = (char *) apr_table_get(sec->cache_pass_table, user);
@@ -904,6 +923,13 @@ static int pg_check_auth(request_rec * r)
 #endif							/* DEBUG_AUTH_PGSQL */
 
 
+	if (!pg_conn) {
+		if (!(pg_conn = pg_connect(sec))) {
+			ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "[mod_auth_pgsql.c] - cannot connect to database");
+			ap_note_basic_auth_failure(r);
+			return HTTP_UNAUTHORIZED;
+		}
+	}
 
 	/* if we cannot do it; leave it to some other guy 
 	 */
@@ -1015,9 +1041,9 @@ pg_log_auth_user(request_rec * r, pg_auth_config_rec * sec, char *user,
 	}
 
 	/* AUD: MAX_STRING_LEN probably isn't always correct */
-	pg_check_string(safe_user, user, strlen(user));
-	pg_check_string(safe_pw, sent_pw, strlen(sent_pw));
-	pg_check_string(safe_req, r->the_request, strlen(r->the_request));
+	pg_check_string(safe_user, user, strlen(user), r, sec);
+	pg_check_string(safe_pw, sent_pw, strlen(sent_pw), r, sec);
+	pg_check_string(safe_req, r->the_request, strlen(r->the_request), r, sec);
 
 
 	if (sec->auth_pg_lowercaseuid) {
diff --git a/mod_auth_pgsql.html b/mod_auth_pgsql.html
index d35768b..5474314 100644
--- a/mod_auth_pgsql.html
+++ b/mod_auth_pgsql.html
@@ -48,6 +48,7 @@ Notes</a> |<a href="#Changelog"> Changelog</a> </p>
   <li><a href="#host">Auth_PG_host</a> </li>
   <li><a href="#port">Auth_PG_port</a> </li>
   <li><a href="#options">Auth_PG_options</a> </li>
+  <li><a href="#charset">Auth_PG_charset</a> </li>
   <li><a href="#database">Auth_PG_database</a> </li>
   <li><a href="#user">Auth_PG_user</a> </li>
   <li><a href="#pwd">Auth_PG_pwd</a> </li>
@@ -104,6 +105,14 @@ be found. </p>
 <p>Specifies an option string to be passed to the postgreSQL backend
 process. Refer to the PostgreSQL user manual for a description of the
 available options. </p>
+<h2><a name="charset"></a> Auth_PG_charset</h2>
+<b>Syntax:</b> Auth_PG_options <i>option string</i><br>
+<b>Context:</b> directory, .htaccess <br>
+<b>Override:</b> AuthConfig <br>
+<b>Status:</b> Extension
+<p>Specifies the name of an encoding to be set for the PostgreSQL
+backend process. Refer to the PostgreSQL user manual for a description
+of the available options. </p>
 <h2><a name="database"></a> Auth_PG_database</h2>
 <b>Syntax:</b> Auth_PG_database <i>database name</i><br>
 <b>Context:</b> directory, .htaccess <br>
