[mod_openssl] remove erroneous SSL_set_shutdown()
[lighttpd.git] / src / mod_vhostdb_pgsql.c
blob5d33c604d5295a5cde144720c088d40b7eee4e29
1 #include "first.h"
3 #include <libpq-fe.h>
5 #include <string.h>
6 #include <stdlib.h>
8 #include "base.h"
9 #include "http_vhostdb.h"
10 #include "log.h"
11 #include "plugin.h"
14 * virtual host plugin using Postgres for domain to directory lookups
17 typedef struct {
18 PGconn *dbconn;
19 buffer *sqlquery;
20 } vhostdb_config;
22 typedef struct {
23 void *vdata;
24 array *options;
25 } plugin_config;
27 typedef struct {
28 PLUGIN_DATA;
29 plugin_config **config_storage;
30 plugin_config conf;
31 } plugin_data;
33 static void mod_vhostdb_dbconf_free (void *vdata)
35 vhostdb_config *dbconf = (vhostdb_config *)vdata;
36 if (!dbconf) return;
37 PQfinish(dbconf->dbconn);
38 free(dbconf);
41 static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata)
43 buffer *sqlquery = NULL;
44 const char *dbname=NULL, *user=NULL, *pass=NULL, *host=NULL, *port=NULL;
46 for (size_t i = 0; i < opts->used; ++i) {
47 const data_string *ds = (data_string *)opts->data[i];
48 if (ds->type == TYPE_STRING) {
49 if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) {
50 sqlquery = ds->value;
51 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) {
52 dbname = ds->value->ptr;
53 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("user"))) {
54 user = ds->value->ptr;
55 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("password"))) {
56 pass = ds->value->ptr;
57 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) {
58 host = ds->value->ptr;
59 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("port"))) {
60 port = ds->value->ptr;
65 /* required:
66 * - sql (sql query)
67 * - dbname
68 * - user (unless dbname is a pgsql conninfo URI)
70 * optional:
71 * - password, default: empty
72 * - hostname
73 * - port, default: 5432
76 if (!buffer_string_is_empty(sqlquery) && NULL != dbname) {
77 vhostdb_config *dbconf;
78 PGconn *dbconn = PQsetdbLogin(host,port,NULL,NULL,dbname,user,pass);
79 if (NULL == dbconn) {
80 log_error_write(srv, __FILE__, __LINE__, "s",
81 "PGsetdbLogin() failed, exiting...");
82 return -1;
85 if (CONNECTION_OK != PQstatus(dbconn)) {
86 log_error_write(srv, __FILE__, __LINE__, "s",
87 "Failed to login to database, exiting...");
88 PQfinish(dbconn);
89 return -1;
92 /* Postgres sets FD_CLOEXEC on database socket descriptors */
94 dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf));
95 dbconf->dbconn = dbconn;
96 dbconf->sqlquery = sqlquery;
97 *vdata = dbconf;
100 return 0;
103 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p);
105 static int mod_vhostdb_pgsql_query(server *srv, connection *con, void *p_d, buffer *docroot)
107 plugin_data *p = (plugin_data *)p_d;
108 vhostdb_config *dbconf;
109 PGresult *res;
110 int cols, rows;
112 /*(reuse buffer for sql query before generating docroot result)*/
113 buffer *sqlquery = docroot;
114 buffer_string_set_length(sqlquery, 0); /*(also resets docroot (alias))*/
116 mod_vhostdb_patch_connection(srv, con, p);
117 if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/
118 dbconf = (vhostdb_config *)p->conf.vdata;
120 for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) {
121 if (NULL != (d = strchr(b, '?'))) {
122 /* escape the uri.authority */
123 size_t len;
124 int err;
125 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
126 buffer_string_prepare_append(sqlquery, buffer_string_length(con->uri.authority) * 2);
127 len = PQescapeStringConn(dbconf->dbconn,
128 sqlquery->ptr + buffer_string_length(sqlquery),
129 CONST_BUF_LEN(con->uri.authority), &err);
130 buffer_commit(sqlquery, len);
131 if (0 != err) return -1;
132 } else {
133 d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery);
134 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
135 break;
139 res = PQexec(dbconf->dbconn, sqlquery->ptr);
141 buffer_string_set_length(docroot, 0); /*(reset buffer to store result)*/
143 if (PGRES_TUPLES_OK != PQresultStatus(res)) {
144 log_error_write(srv, __FILE__, __LINE__, "s",
145 PQerrorMessage(dbconf->dbconn));
146 PQclear(res);
147 return -1;
150 cols = PQnfields(res);
151 rows = PQntuples(res);
152 if (rows == 1 && cols >= 1) {
153 buffer_copy_string(docroot, PQgetvalue(res, 0, 0));
154 } /* else no such virtual host */
156 PQclear(res);
157 return 0;
163 INIT_FUNC(mod_vhostdb_init) {
164 static http_vhostdb_backend_t http_vhostdb_backend_pgsql =
165 { "pgsql", mod_vhostdb_pgsql_query, NULL };
166 plugin_data *p = calloc(1, sizeof(*p));
168 /* register http_vhostdb_backend_pgsql */
169 http_vhostdb_backend_pgsql.p_d = p;
170 http_vhostdb_backend_set(&http_vhostdb_backend_pgsql);
172 return p;
175 FREE_FUNC(mod_vhostdb_cleanup) {
176 plugin_data *p = p_d;
177 if (!p) return HANDLER_GO_ON;
179 if (p->config_storage) {
180 for (size_t i = 0; i < srv->config_context->used; i++) {
181 plugin_config *s = p->config_storage[i];
182 if (!s) continue;
183 mod_vhostdb_dbconf_free(s->vdata);
184 array_free(s->options);
185 free(s);
187 free(p->config_storage);
189 free(p);
191 UNUSED(srv);
192 return HANDLER_GO_ON;
195 SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
196 plugin_data *p = p_d;
198 config_values_t cv[] = {
199 { "vhostdb.pgsql", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
200 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
203 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
205 for (size_t i = 0; i < srv->config_context->used; ++i) {
206 data_config const *config = (data_config const*)srv->config_context->data[i];
207 plugin_config *s = calloc(1, sizeof(plugin_config));
209 s->options = array_init();
210 cv[0].destination = s->options;
212 p->config_storage[i] = s;
214 if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
215 return HANDLER_ERROR;
218 if (!array_is_kvstring(s->options)) {
219 log_error_write(srv, __FILE__, __LINE__, "s",
220 "unexpected value for vhostdb.pgsql; expected list of \"option\" => \"value\"");
221 return HANDLER_ERROR;
224 if (s->options->used
225 && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) {
226 return HANDLER_ERROR;
230 return HANDLER_GO_ON;
233 #define PATCH(x) \
234 p->conf.x = s->x;
235 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p)
237 plugin_config *s = p->config_storage[0];
238 PATCH(vdata);
240 /* skip the first, the global context */
241 for (size_t i = 1; i < srv->config_context->used; ++i) {
242 data_config *dc = (data_config *)srv->config_context->data[i];
243 s = p->config_storage[i];
245 /* condition didn't match */
246 if (!config_check_cond(srv, con, dc)) continue;
248 /* merge config */
249 for (size_t j = 0; j < dc->value->used; ++j) {
250 data_unset *du = dc->value->data[j];
252 if (buffer_is_equal_string(du->key,CONST_STR_LEN("vhostdb.pgsql"))){
253 PATCH(vdata);
258 #undef PATCH
260 /* this function is called at dlopen() time and inits the callbacks */
261 int mod_vhostdb_pgsql_plugin_init (plugin *p);
262 int mod_vhostdb_pgsql_plugin_init (plugin *p)
264 p->version = LIGHTTPD_VERSION_ID;
265 p->name = buffer_init_string("vhostdb_pgsql");
267 p->init = mod_vhostdb_init;
268 p->cleanup = mod_vhostdb_cleanup;
269 p->set_defaults = mod_vhostdb_set_defaults;
271 return 0;