1 /* $Id: mod_log_sql_pgsql.c 165 2005-02-09 01:25:40Z urkle@drip.ws $ */
2 
3 #if defined(WITH_APACHE20)
4 #	include "apache20.h"
5 #elif defined(WITH_APACHE13)
6 #	include "apache13.h"
7 #else
8 #	error Unsupported Apache version
9 #endif
10 
11 
12 #ifdef HAVE_CONFIG_H
13 /* Undefine these to prevent conflicts between Apache ap_config_auto.h and
14  * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
15  */
16 #undef PACKAGE_BUGREPORT
17 #undef PACKAGE_NAME
18 #undef PACKAGE_STRING
19 #undef PACKAGE_TARNAME
20 #undef PACKAGE_VERSION
21 
22 #include "config.h"
23 #endif
24 
25 #include "mod_log_sql.h"
26 
27 #include "libpq-fe.h"
28 
29 /* Connect to the PGSQL database */
log_sql_pgsql_connect(server_rec * s,logsql_dbconnection * db)30 static logsql_opendb_ret log_sql_pgsql_connect(server_rec *s, logsql_dbconnection *db)
31 {
32 	const char *host = apr_table_get(db->parms,"hostname");
33 	const char *user = apr_table_get(db->parms,"username");
34 	const char *passwd = apr_table_get(db->parms,"password");
35 	const char *database = apr_table_get(db->parms,"database");
36 	const char *s_tcpport = apr_table_get(db->parms,"port");
37 
38 	db->handle = PQsetdbLogin(host, s_tcpport, NULL, NULL, database, user, passwd);
39 
40 	if (PQstatus(db->handle) == CONNECTION_OK) {
41 		log_error(APLOG_MARK,APLOG_DEBUG,0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'",
42 				host, s_tcpport, database, user);
43 		return LOGSQL_OPENDB_SUCCESS;
44 	} else {
45 		log_error(APLOG_MARK,APLOG_DEBUG,0, s,"mod_log_sql: database connection error: %s",
46 				PQerrorMessage(db->handle));
47 		log_error(APLOG_MARK,APLOG_DEBUG, 0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'",
48 				host, s_tcpport, database, user);
49 		return LOGSQL_OPENDB_FAIL;
50 	}
51 }
52 
53 /* Close the DB link */
log_sql_pgsql_close(logsql_dbconnection * db)54 static void log_sql_pgsql_close(logsql_dbconnection *db)
55 {
56 	PQfinish((PGconn*)(db->handle));
57 }
58 
59 /* Routine to escape the 'dangerous' characters that would otherwise
60  * corrupt the INSERT string: ', \, and "
61  * Also PQescapeString does not place the ' around the string. So we have
62  * to do this manually
63  */
log_sql_pgsql_escape(const char * from_str,apr_pool_t * p,logsql_dbconnection * db)64 static const char *log_sql_pgsql_escape(const char *from_str, apr_pool_t *p,
65 								logsql_dbconnection *db)
66 {
67 	char *temp;
68 	if (!from_str)
69 		return NULL;
70 	else {
71 	  	char *to_str;
72 		unsigned long length = strlen(from_str);
73 		unsigned long retval;
74 
75 		/* Pre-allocate a new string that could hold twice the original, which would only
76 		 * happen if the whole original string was 'dangerous' characters.
77 		 * And forsee the space for the 2 '
78 		 */
79 		temp = to_str = (char *) apr_palloc(p, length * 2 + 3);
80 		if (!to_str) {
81 			return from_str;
82 		}
83 
84 		*temp = '\'';
85 		temp++;
86 
87 		retval = PQescapeString(temp, from_str, length);
88 
89 		/* avoid the string to be tolong for the sql database*/
90 		if (retval > 250) retval = 250;
91 
92 		*(temp+retval) = '\'';
93 		*(temp+retval+1) = '\0';
94 
95 		/* We must always return the to_str, because we always need the ' added */
96 //		if (retval)
97 		  return to_str;
98 //		else
99 //		  return from_str;
100 	}
101 }
102 
103 /* Run a sql insert query and return a categorized error or success */
log_sql_pgsql_query(request_rec * r,logsql_dbconnection * db,const char * query)104 static logsql_query_ret log_sql_pgsql_query(request_rec *r,logsql_dbconnection *db,
105 								const char *query)
106 {
107 	PGresult *result;
108 	void (*handler) (int);
109 	unsigned int real_error = 0;
110 	/*const char *real_error_str = NULL;*/
111 
112 	PGconn *conn = db->handle;
113 
114 	if (PQstatus(conn) != CONNECTION_OK) {
115 		return LOGSQL_QUERY_NOLINK;
116 	}
117 	/* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
118 	/* Does postgresql do this also ??? */
119 	handler = signal(SIGPIPE, SIG_IGN);
120 
121 	result = PQexec(conn, query);
122 	/* Run the query */
123 	if (PQresultStatus(result) == PGRES_COMMAND_OK) {
124 		signal(SIGPIPE, handler);
125 		PQclear(result);
126 		return LOGSQL_QUERY_SUCCESS;
127 	}
128 	/* Check to see if the error is "nonexistent table" */
129 	/*	removed ... don't know how ! (sorry)
130 	real_error = mysql_errno(dblink);
131 
132 	if (real_error == ER_NO_SUCH_TABLE) {
133 		log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query");
134 		signal(SIGPIPE, handler);
135 		PQclear(result);
136 		return LOGSQL_QUERY_NOTABLE;
137 	}*/
138 
139 	/* Restore SIGPIPE to its original handler function */
140 	signal(SIGPIPE, handler);
141 	PQclear(result);
142 	return LOGSQL_QUERY_FAIL;
143 }
144 
145 /* Create table table_name of type table_type. */
log_sql_pgsql_create(request_rec * r,logsql_dbconnection * db,logsql_tabletype table_type,const char * table_name)146 static logsql_table_ret log_sql_pgsql_create(request_rec *r, logsql_dbconnection *db,
147 						logsql_tabletype table_type, const char *table_name)
148 {
149 	PGresult *result;
150 	const char *tabletype = apr_table_get(db->parms,"tabletype");
151 	void (*handler) (int);
152 	char *type_suffix = NULL;
153 
154 	char *create_prefix = "create table if not exists `";
155 	char *create_suffix = NULL;
156 	char *create_sql;
157 
158 	PGconn *conn = db->handle;
159 
160 /*	if (!global_config.createtables) {
161 		return APR_SUCCESS;
162 	}*/
163 
164 	switch (table_type) {
165 	case LOGSQL_TABLE_ACCESS:
166 		create_suffix =
167 	"` (id char(19),\
168        agent varchar(255),\
169        bytes_sent int unsigned,\
170        child_pid smallint unsigned,\
171        cookie varchar(255),\
172 	   machine_id varchar(25),\
173        request_file varchar(255),\
174        referer varchar(255),\
175        remote_host varchar(50),\
176        remote_logname varchar(50),\
177        remote_user varchar(50),\
178        request_duration smallint unsigned,\
179        request_line varchar(255),\
180        request_method varchar(10),\
181        request_protocol varchar(10),\
182        request_time char(28),\
183        request_uri varchar(255),\
184 	   request_args varchar(255),\
185        server_port smallint unsigned,\
186        ssl_cipher varchar(25),\
187        ssl_keysize smallint unsigned,\
188        ssl_maxkeysize smallint unsigned,\
189        status smallint unsigned,\
190        time_stamp int unsigned,\
191        virtual_host varchar(255))";
192 		break;
193 	case LOGSQL_TABLE_COOKIES:
194 	case LOGSQL_TABLE_HEADERSIN:
195 	case LOGSQL_TABLE_HEADERSOUT:
196 	case LOGSQL_TABLE_NOTES:
197 		create_suffix =
198 	"` (id char(19),\
199 	   item varchar(80),\
200 	   val varchar(80))";
201 		break;
202 	}
203 
204 	if (tabletype) {
205 		type_suffix = apr_pstrcat(r->pool, " TYPE=",
206 							tabletype, NULL);
207 	}
208 	/* Find memory long enough to hold the whole CREATE string + \0 */
209 	create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix,
210 						type_suffix, NULL);
211 
212 	log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql);
213 
214 	if (PQstatus(conn) != CONNECTION_OK) {
215 		return LOGSQL_QUERY_NOLINK;
216 	}
217 	/* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
218 	handler = signal(SIGPIPE, SIG_IGN);
219 
220 	/* Run the create query */
221 	result = PQexec(conn, create_sql);
222   	if (PQresultStatus(result) != PGRES_COMMAND_OK) {
223 		log_error(APLOG_MARK,APLOG_ERR,0, r->server,"failed to create table: %s",
224 			table_name);
225 		signal(SIGPIPE, handler);
226 		PQclear(result);
227 		return LOGSQL_TABLE_FAIL;
228 	}
229 	signal(SIGPIPE, handler);
230 	PQclear(result);
231 	return LOGSQL_TABLE_SUCCESS;
232 }
233 
234 static char *supported_drivers[] = {"pgsql",NULL};
235 static logsql_dbdriver pgsql_driver = {
236 	supported_drivers,
237 	log_sql_pgsql_connect,	/* open DB connection */
238 	log_sql_pgsql_close,	/* close DB connection */
239 	log_sql_pgsql_escape,	/* escape query */
240 	log_sql_pgsql_query,	/* insert query */
241 	log_sql_pgsql_create	/* create table */
242 };
243 
LOGSQL_REGISTER(pgsql)244 LOGSQL_REGISTER(pgsql) {
245 	log_sql_register_driver(p,&pgsql_driver);
246 	LOGSQL_REGISTER_RETURN;
247 }
248