[core] consolidate backend network write handlers
[lighttpd.git] / src / mod_vhostdb_mysql.c
blob4b2877c59ec9c52a988baf901af7ab6fda38149d
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 "fdevent.h"
11 #include "log.h"
12 #include "plugin.h"
15 * virtual host plugin using MySQL for domain to directory lookups
18 typedef struct {
19 MYSQL *dbconn;
20 buffer *sqlquery;
21 } vhostdb_config;
23 typedef struct {
24 void *vdata;
25 array *options;
26 } plugin_config;
28 typedef struct {
29 PLUGIN_DATA;
30 plugin_config **config_storage;
31 plugin_config conf;
32 } plugin_data;
34 static void mod_vhostdb_dbconf_free (void *vdata)
36 vhostdb_config *dbconf = (vhostdb_config *)vdata;
37 if (!dbconf) return;
38 mysql_close(dbconf->dbconn);
39 free(dbconf);
42 static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata)
44 buffer *sqlquery = NULL;
45 const char *dbname=NULL, *user=NULL, *pass=NULL, *host=NULL, *sock=NULL;
46 unsigned int port = 0;
48 for (size_t i = 0; i < opts->used; ++i) {
49 const data_string *ds = (data_string *)opts->data[i];
50 if (ds->type == TYPE_STRING && !buffer_string_is_empty(ds->value)) {
51 if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) {
52 sqlquery = ds->value;
53 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) {
54 dbname = ds->value->ptr;
55 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("user"))) {
56 user = ds->value->ptr;
57 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("password"))) {
58 pass = ds->value->ptr;
59 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) {
60 host = ds->value->ptr;
61 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("port"))) {
62 port = strtoul(ds->value->ptr, NULL, 10);
63 } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sock"))) {
64 sock = ds->value->ptr;
69 /* required:
70 * - sql (sql query)
71 * - dbname
72 * - user
74 * optional:
75 * - password, default: empty
76 * - socket, default: mysql default
77 * - hostname, if set overrides socket
78 * - port, default: 3306
81 if (!buffer_string_is_empty(sqlquery)
82 && dbname && *dbname && user && *user) {
83 vhostdb_config *dbconf;
84 MYSQL *dbconn = mysql_init(NULL);
85 if (NULL == dbconn) {
86 log_error_write(srv, __FILE__, __LINE__, "s",
87 "mysql_init() failed, exiting...");
88 return -1;
91 #if MYSQL_VERSION_ID >= 50013
92 /* in mysql versions above 5.0.3 the reconnect flag is off by default */
94 my_bool reconnect = 1;
95 mysql_options(dbconn, MYSQL_OPT_RECONNECT, &reconnect);
97 #endif
99 /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */
100 #if MYSQL_VERSION_ID < 40100
101 #ifndef CLIENT_MULTI_STATEMENTS
102 #define CLIENT_MULTI_STATEMENTS 0
103 #endif
104 #endif
105 if (!mysql_real_connect(dbconn, host, user, pass, dbname, port, sock,
106 CLIENT_MULTI_STATEMENTS)) {
107 log_error_write(srv, __FILE__, __LINE__, "s",
108 mysql_error(dbconn));
109 mysql_close(dbconn);
110 return -1;
113 fdevent_setfd_cloexec(dbconn->net.fd);
115 dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf));
116 dbconf->dbconn = dbconn;
117 dbconf->sqlquery = sqlquery;
118 *vdata = dbconf;
121 return 0;
124 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p);
126 static int mod_vhostdb_mysql_query(server *srv, connection *con, void *p_d, buffer *docroot)
128 plugin_data *p = (plugin_data *)p_d;
129 vhostdb_config *dbconf;
130 unsigned cols;
131 MYSQL_ROW row;
132 MYSQL_RES *result;
134 /*(reuse buffer for sql query before generating docroot result)*/
135 buffer *sqlquery = docroot;
136 buffer_string_set_length(sqlquery, 0); /*(also resets docroot (alias))*/
138 mod_vhostdb_patch_connection(srv, con, p);
139 if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/
140 dbconf = (vhostdb_config *)p->conf.vdata;
142 for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) {
143 if (NULL != (d = strchr(b, '?'))) {
144 /* escape the uri.authority */
145 unsigned long len;
146 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
147 buffer_string_prepare_append(sqlquery, buffer_string_length(con->uri.authority) * 2);
148 len = mysql_real_escape_string(dbconf->dbconn,
149 sqlquery->ptr + buffer_string_length(sqlquery),
150 CONST_BUF_LEN(con->uri.authority));
151 if ((unsigned long)~0 == len) return -1;
152 buffer_commit(sqlquery, len);
153 } else {
154 d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery);
155 buffer_append_string_len(sqlquery, b, (size_t)(d - b));
156 break;
160 if (mysql_real_query(dbconf->dbconn, CONST_BUF_LEN(sqlquery))) {
161 log_error_write(srv, __FILE__, __LINE__, "s",
162 mysql_error(dbconf->dbconn));
163 buffer_string_set_length(docroot, 0); /*(reset buffer; no result)*/
164 return -1;
167 buffer_string_set_length(docroot, 0); /*(reset buffer to store result)*/
169 result = mysql_store_result(dbconf->dbconn);
170 cols = mysql_num_fields(result);
171 row = mysql_fetch_row(result);
172 if (row && cols >= 1) {
173 buffer_copy_string(docroot, row[0]);
174 } /* else no such virtual host */
176 mysql_free_result(result);
177 #if MYSQL_VERSION_ID >= 40100
178 while (0 == mysql_next_result(dbconf->dbconn)) ;
179 #endif
180 return 0;
186 INIT_FUNC(mod_vhostdb_init) {
187 static http_vhostdb_backend_t http_vhostdb_backend_mysql =
188 { "mysql", mod_vhostdb_mysql_query, NULL };
189 plugin_data *p = calloc(1, sizeof(*p));
191 /* register http_vhostdb_backend_mysql */
192 http_vhostdb_backend_mysql.p_d = p;
193 http_vhostdb_backend_set(&http_vhostdb_backend_mysql);
195 return p;
198 FREE_FUNC(mod_vhostdb_cleanup) {
199 plugin_data *p = p_d;
200 if (!p) return HANDLER_GO_ON;
202 if (p->config_storage) {
203 for (size_t i = 0; i < srv->config_context->used; i++) {
204 plugin_config *s = p->config_storage[i];
205 if (!s) continue;
206 mod_vhostdb_dbconf_free(s->vdata);
207 array_free(s->options);
208 free(s);
210 free(p->config_storage);
212 free(p);
214 UNUSED(srv);
215 return HANDLER_GO_ON;
218 SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) {
219 plugin_data *p = p_d;
221 config_values_t cv[] = {
222 { "vhostdb.mysql", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
223 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
226 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
228 for (size_t i = 0; i < srv->config_context->used; ++i) {
229 data_config const *config = (data_config const*)srv->config_context->data[i];
230 plugin_config *s = calloc(1, sizeof(plugin_config));
232 s->options = array_init();
233 cv[0].destination = s->options;
235 p->config_storage[i] = s;
237 if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
238 return HANDLER_ERROR;
241 if (!array_is_kvstring(s->options)) {
242 log_error_write(srv, __FILE__, __LINE__, "s",
243 "unexpected value for vhostdb.mysql; expected list of \"option\" => \"value\"");
244 return HANDLER_ERROR;
247 if (s->options->used
248 && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) {
249 return HANDLER_ERROR;
253 return HANDLER_GO_ON;
256 #define PATCH(x) \
257 p->conf.x = s->x;
258 static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p)
260 plugin_config *s = p->config_storage[0];
261 PATCH(vdata);
263 /* skip the first, the global context */
264 for (size_t i = 1; i < srv->config_context->used; ++i) {
265 data_config *dc = (data_config *)srv->config_context->data[i];
266 s = p->config_storage[i];
268 /* condition didn't match */
269 if (!config_check_cond(srv, con, dc)) continue;
271 /* merge config */
272 for (size_t j = 0; j < dc->value->used; ++j) {
273 data_unset *du = dc->value->data[j];
275 if (buffer_is_equal_string(du->key,CONST_STR_LEN("vhostdb.mysql"))){
276 PATCH(vdata);
281 #undef PATCH
283 /* this function is called at dlopen() time and inits the callbacks */
284 int mod_vhostdb_mysql_plugin_init (plugin *p);
285 int mod_vhostdb_mysql_plugin_init (plugin *p)
287 p->version = LIGHTTPD_VERSION_ID;
288 p->name = buffer_init_string("vhostdb_mysql");
290 p->init = mod_vhostdb_init;
291 p->cleanup = mod_vhostdb_cleanup;
292 p->set_defaults = mod_vhostdb_set_defaults;
294 return 0;