mod_vhostdb* (dbi,mysql,pgsql,ldap) (fixes #485, fixes #1936, fixes #2297)
[lighttpd.git] / src / mod_vhostdb_mysql.c
blob5490cef6b764a66096669e73ab2b6751fb0f7e88
1 #include "first.h"
3 #include <mysql.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 MySQL for domain to directory lookups
17 typedef struct {
18 MYSQL *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 mysql_close(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, *sock=NULL;
45 unsigned int port = 0;
47 for (size_t i = 0; i < opts->used; ++i) {
48 const data_string *ds = (data_string *)opts->data[i];
49 if (ds->type == TYPE_STRING && !buffer_string_is_empty(ds->value)) {
50 if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) {
51 sqlquery = ds->value;
52 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) {
53 dbname = ds->value->ptr;
54 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("user"))) {
55 user = ds->value->ptr;
56 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("password"))) {
57 pass = ds->value->ptr;
58 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) {
59 host = ds->value->ptr;
60 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("port"))) {
61 port = strtoul(ds->value->ptr, NULL, 10);
62 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sock"))) {
63 sock = ds->value->ptr;
68 /* required:
69 * - sql (sql query)
70 * - dbname
71 * - user
73 * optional:
74 * - password, default: empty
75 * - socket, default: mysql default
76 * - hostname, if set overrides socket
77 * - port, default: 3306
80 if (!buffer_string_is_empty(sqlquery)
81 && dbname && *dbname && user && *user) {
82 vhostdb_config *dbconf;
83 MYSQL *dbconn = mysql_init(NULL);
84 if (NULL == dbconn) {
85 log_error_write(srv, __FILE__, __LINE__, "s",
86 "mysql_init() failed, exiting...");
87 return -1;
90 #if MYSQL_VERSION_ID >= 50013
91 /* in mysql versions above 5.0.3 the reconnect flag is off by default */
93 my_bool reconnect = 1;
94 mysql_options(dbconn, MYSQL_OPT_RECONNECT, &reconnect);
96 #endif
98 /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */
99 #if MYSQL_VERSION_ID < 40100
100 #ifndef CLIENT_MULTI_STATEMENTS
101 #define CLIENT_MULTI_STATEMENTS 0
102 #endif
103 #endif
104 if (!mysql_real_connect(dbconn, host, user, pass, dbname, port, sock,
105 CLIENT_MULTI_STATEMENTS)) {
106 log_error_write(srv, __FILE__, __LINE__, "s",
107 mysql_error(dbconn));
108 mysql_close(dbconn);
109 return -1;
112 fd_close_on_exec(dbconn->net.fd);
114 dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf));
115 dbconf->dbconn = dbconn;
116 dbconf->sqlquery = sqlquery;
117 *vdata = dbconf;
120 return 0;
123 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p);
125 static int mod_vhostdb_mysql_query(server *srv, connection *con, void *p_d, buffer *docroot)
127 plugin_data *p = (plugin_data *)p_d;
128 vhostdb_config *dbconf;
129 unsigned cols;
130 MYSQL_ROW row;
131 MYSQL_RES *result;
133 /*(reuse buffer for sql query before generating docroot result)*/
134 buffer *sqlquery = docroot;
135 buffer_string_set_length(sqlquery, 0); /*(also resets docroot (alias))*/
137 mod_vhostdb_patch_connection(srv, con, p);
138 if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/
139 dbconf = (vhostdb_config *)p->conf.vdata;
141 for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) {
142 if (NULL != (d = strchr(b, '?'))) {
143 /* escape the uri.authority */
144 unsigned long len;
145 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
146 buffer_string_prepare_append(sqlquery, buffer_string_length(con->uri.authority) * 2);
147 len = mysql_real_escape_string(dbconf->dbconn,
148 sqlquery->ptr + buffer_string_length(sqlquery),
149 CONST_BUF_LEN(con->uri.authority));
150 if ((unsigned long)~0 == len) return -1;
151 buffer_commit(sqlquery, len);
152 } else {
153 d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery);
154 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
155 break;
159 if (mysql_real_query(dbconf->dbconn, CONST_BUF_LEN(sqlquery))) {
160 log_error_write(srv, __FILE__, __LINE__, "s",
161 mysql_error(dbconf->dbconn));
162 buffer_string_set_length(docroot, 0); /*(reset buffer; no result)*/
163 return -1;
166 buffer_string_set_length(docroot, 0); /*(reset buffer to store result)*/
168 result = mysql_store_result(dbconf->dbconn);
169 cols = mysql_num_fields(result);
170 row = mysql_fetch_row(result);
171 if (row && cols >= 1) {
172 buffer_copy_string(docroot, row[0]);
173 } /* else no such virtual host */
175 mysql_free_result(result);
176 #if MYSQL_VERSION_ID >= 40100
177 while (0 == mysql_next_result(dbconf->dbconn)) ;
178 #endif
179 return 0;
185 INIT_FUNC(mod_vhostdb_init) {
186 static http_vhostdb_backend_t http_vhostdb_backend_mysql =
187 { "mysql", mod_vhostdb_mysql_query, NULL };
188 plugin_data *p = calloc(1, sizeof(*p));
190 /* register http_vhostdb_backend_mysql */
191 http_vhostdb_backend_mysql.p_d = p;
192 http_vhostdb_backend_set(&http_vhostdb_backend_mysql);
194 return p;
197 FREE_FUNC(mod_vhostdb_cleanup) {
198 plugin_data *p = p_d;
199 if (!p) return HANDLER_GO_ON;
201 if (p->config_storage) {
202 for (size_t i = 0; i < srv->config_context->used; i++) {
203 plugin_config *s = p->config_storage[i];
204 if (!s) continue;
205 mod_vhostdb_dbconf_free(s->vdata);
206 array_free(s->options);
207 free(s);
209 free(p->config_storage);
211 free(p);
213 UNUSED(srv);
214 return HANDLER_GO_ON;
217 SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
218 plugin_data *p = p_d;
220 config_values_t cv[] = {
221 { "vhostdb.mysql", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
222 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
225 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
227 for (size_t i = 0; i < srv->config_context->used; ++i) {
228 data_config const *config = (data_config const*)srv->config_context->data[i];
229 plugin_config *s = calloc(1, sizeof(plugin_config));
231 s->options = array_init();
232 cv[0].destination = s->options;
234 p->config_storage[i] = s;
236 if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
237 return HANDLER_ERROR;
240 if (s->options->used
241 && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) {
242 return HANDLER_ERROR;
246 return HANDLER_GO_ON;
249 #define PATCH(x) \
250 p->conf.x = s->x;
251 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p)
253 plugin_config *s = p->config_storage[0];
254 PATCH(vdata);
256 /* skip the first, the global context */
257 for (size_t i = 1; i < srv->config_context->used; ++i) {
258 data_config *dc = (data_config *)srv->config_context->data[i];
259 s = p->config_storage[i];
261 /* condition didn't match */
262 if (!config_check_cond(srv, con, dc)) continue;
264 /* merge config */
265 for (size_t j = 0; j < dc->value->used; ++j) {
266 data_unset *du = dc->value->data[j];
268 if (buffer_is_equal_string(du->key,CONST_STR_LEN("vhostdb.mysql"))){
269 PATCH(vdata);
274 #undef PATCH
276 /* this function is called at dlopen() time and inits the callbacks */
277 int mod_vhostdb_mysql_plugin_init (plugin *p);
278 int mod_vhostdb_mysql_plugin_init (plugin *p)
280 p->version = LIGHTTPD_VERSION_ID;
281 p->name = buffer_init_string("vhostdb_mysql");
283 p->init = mod_vhostdb_init;
284 p->cleanup = mod_vhostdb_cleanup;
285 p->set_defaults = mod_vhostdb_set_defaults;
287 return 0;