9 #include "http_vhostdb.h"
15 * virtual host plugin using MySQL for domain to directory lookups
30 plugin_config
**config_storage
;
34 static void mod_vhostdb_dbconf_free (void *vdata
)
36 vhostdb_config
*dbconf
= (vhostdb_config
*)vdata
;
38 mysql_close(dbconf
->dbconn
);
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"))) {
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
;
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
);
86 log_error_write(srv
, __FILE__
, __LINE__
, "s",
87 "mysql_init() failed, exiting...");
91 #if MYSQL_VERSION_ID >= 50013
92 /* in mysql versions above 5.0.3 the reconnect flag is off by default */
95 mysql_options(dbconn
, MYSQL_OPT_RECONNECT
, &reconnect
);
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
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
));
113 fdevent_setfd_cloexec(dbconn
->net
.fd
);
115 dbconf
= (vhostdb_config
*)calloc(1, sizeof(*dbconf
));
116 dbconf
->dbconn
= dbconn
;
117 dbconf
->sqlquery
= sqlquery
;
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
;
134 /*(reuse buffer for sql query before generating docroot result)*/
135 buffer
*sqlquery
= docroot
;
136 buffer_clear(sqlquery
); /*(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 */
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
);
154 d
= dbconf
->sqlquery
->ptr
+ buffer_string_length(dbconf
->sqlquery
);
155 buffer_append_string_len(sqlquery
, b
, (size_t)(d
- b
));
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_clear(docroot
); /*(reset buffer; no result)*/
167 buffer_clear(docroot
); /*(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
)) ;
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
);
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
];
206 mod_vhostdb_dbconf_free(s
->vdata
);
207 array_free(s
->options
);
210 free(p
->config_storage
);
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(srv
->config_context
->used
, sizeof(plugin_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
;
248 && 0 != mod_vhostdb_dbconf_setup(srv
, s
->options
, &s
->vdata
)) {
249 return HANDLER_ERROR
;
253 return HANDLER_GO_ON
;
258 static void mod_vhostdb_patch_connection (server
*srv
, connection
*con
, plugin_data
*p
)
260 plugin_config
*s
= p
->config_storage
[0];
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;
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"))){
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
;