xref: /lighttpd1.4/src/mod_simple_vhost.c (revision 70b1af06)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 #include "stat_cache.h"
7 
8 #include "plugin.h"
9 
10 #include <stdlib.h>
11 #include <string.h>
12 
13 typedef struct {
14     const buffer *server_root;
15     const buffer *default_host;
16     const buffer *document_root;
17     unsigned short debug;
18 } plugin_config;
19 
20 typedef struct {
21     PLUGIN_DATA;
22     plugin_config defaults;
23     plugin_config conf;
24 
25     buffer tmp_buf;
26     buffer last_root;
27 } plugin_data;
28 
29 INIT_FUNC(mod_simple_vhost_init) {
30     return calloc(1, sizeof(plugin_data));
31 }
32 
33 FREE_FUNC(mod_simple_vhost_free) {
34     plugin_data *p = p_d;
35     free(p->tmp_buf.ptr);
36     free(p->last_root.ptr);
37 }
38 
39 static void mod_simple_vhost_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
40     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
41       case 0: /* simple-vhost.server-root */
42         pconf->server_root = cpv->v.b;
43         break;
44       case 1: /* simple-vhost.default-host */
45         pconf->default_host = cpv->v.b;
46         break;
47       case 2: /* simple-vhost.document-root */
48         pconf->document_root = cpv->v.b;
49         break;
50       case 3: /* simple-vhost.debug */
51         pconf->debug = cpv->v.shrt;
52         break;
53       default:/* should not happen */
54         return;
55     }
56 }
57 
58 static void mod_simple_vhost_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
59     do {
60         mod_simple_vhost_merge_config_cpv(pconf, cpv);
61     } while ((++cpv)->k_id != -1);
62 }
63 
64 static void mod_simple_vhost_patch_config(request_st * const r, plugin_data * const p) {
65     p->conf = p->defaults; /* copy small struct instead of memcpy() */
66     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
67     for (int i = 1, used = p->nconfig; i < used; ++i) {
68         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
69             mod_simple_vhost_merge_config(&p->conf,
70                                           p->cvlist + p->cvlist[i].v.u2[0]);
71     }
72 }
73 
74 SETDEFAULTS_FUNC(mod_simple_vhost_set_defaults) {
75     static const config_plugin_keys_t cpk[] = {
76       { CONST_STR_LEN("simple-vhost.server-root"),
77         T_CONFIG_STRING,
78         T_CONFIG_SCOPE_CONNECTION }
79      ,{ CONST_STR_LEN("simple-vhost.default-host"),
80         T_CONFIG_STRING,
81         T_CONFIG_SCOPE_CONNECTION }
82      ,{ CONST_STR_LEN("simple-vhost.document-root"),
83         T_CONFIG_STRING,
84         T_CONFIG_SCOPE_CONNECTION }
85      ,{ CONST_STR_LEN("simple-vhost.debug"),
86         T_CONFIG_SHORT,
87         T_CONFIG_SCOPE_CONNECTION }
88      ,{ NULL, 0,
89         T_CONFIG_UNSET,
90         T_CONFIG_SCOPE_UNSET }
91     };
92 
93     plugin_data * const p = p_d;
94     if (!config_plugin_values_init(srv, p, cpk, "mod_simple_vhost"))
95         return HANDLER_ERROR;
96 
97     /* process and validate config directives
98      * (init i to 0 if global context; to 1 to skip empty global context) */
99     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
100         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
101         for (; -1 != cpv->k_id; ++cpv) {
102             switch (cpv->k_id) {
103               case 0: /* simple-vhost.server-root */
104               case 2: /* simple-vhost.document-root */
105                 if (!buffer_string_is_empty(cpv->v.b)) {
106                     buffer *b;
107                     *(const buffer **)&b = cpv->v.b;
108                     buffer_append_slash(b);
109                 }
110                 break;
111               case 1: /* simple-vhost.default-host */
112               case 3: /* simple-vhost.debug */
113                 break;
114               default:/* should not happen */
115                 break;
116             }
117         }
118     }
119 
120     /* initialize p->defaults from global config context */
121     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
122         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
123         if (-1 != cpv->k_id)
124             mod_simple_vhost_merge_config(&p->defaults, cpv);
125     }
126 
127     return HANDLER_GO_ON;
128 }
129 
130 static void build_doc_root_path(buffer *out, const buffer *sroot, const buffer *host, const buffer *droot) {
131 	force_assert(!buffer_string_is_empty(sroot));
132 	buffer_copy_buffer(out, sroot);
133 
134 	if (!buffer_string_is_empty(host)) {
135 		/* a hostname has to start with a alpha-numerical character
136 		 * and must not contain a slash "/"
137 		 */
138 		char *dp;
139 		if (NULL == (dp = strchr(host->ptr, ':'))) {
140 			buffer_append_string_buffer(out, host);
141 		} else {
142 			buffer_append_string_len(out, host->ptr, dp - host->ptr);
143 		}
144 	}
145 
146 	if (!buffer_string_is_empty(droot)) {
147 		buffer_append_path_len(out, CONST_BUF_LEN(droot));
148 	}
149 	else {
150 		buffer_append_slash(out);
151 	}
152 }
153 
154 static int build_doc_root(request_st * const r, plugin_data *p, buffer *out, const buffer *host) {
155 	stat_cache_entry *sce = NULL;
156 
157 	build_doc_root_path(out, p->conf.server_root, host, p->conf.document_root);
158 
159 	/* one-element cache (positive cache, not negative cache) */
160 	if (buffer_is_equal(out, &p->last_root)) return 1;
161 
162 	sce = stat_cache_get_entry(out);
163 	if (NULL == sce) {
164 		if (p->conf.debug) {
165 			log_perror(r->conf.errh, __FILE__, __LINE__, "%s", out->ptr);
166 		}
167 		return 0;
168 	} else if (!S_ISDIR(sce->st.st_mode)) {
169 		return 0;
170 	}
171 
172 	buffer_copy_buffer(&p->last_root, out);
173 	return 1;
174 }
175 
176 static handler_t mod_simple_vhost_docroot(request_st * const r, void *p_data) {
177     plugin_data * const p = p_data;
178     mod_simple_vhost_patch_config(r, p);
179     if (buffer_string_is_empty(p->conf.server_root)) return HANDLER_GO_ON;
180     /* build_doc_root() requires p->conf.server_root;
181      * skip module if simple-vhost.server-root not set or set to empty string */
182 
183     /* (default host and HANDLER_GO_ON instead of HANDLER_ERROR (on error)
184      *  are the two differences between mod_simple_vhost and mod_vhostdb) */
185 
186     /* build document-root */
187     buffer * const b = &p->tmp_buf;
188     const buffer *host = &r->uri.authority;
189     if ((!buffer_string_is_empty(host) && build_doc_root(r, p, b, host))
190         || build_doc_root(r, p, b, (host = p->conf.default_host))) {
191         r->server_name = &r->server_name_buf;
192         buffer_copy_buffer(&r->server_name_buf, host);
193         buffer_copy_buffer(&r->physical.doc_root, b);
194     }
195 
196     return HANDLER_GO_ON;
197 }
198 
199 
200 int mod_simple_vhost_plugin_init(plugin *p);
201 int mod_simple_vhost_plugin_init(plugin *p) {
202 	p->version     = LIGHTTPD_VERSION_ID;
203 	p->name        = "simple_vhost";
204 
205 	p->init        = mod_simple_vhost_init;
206 	p->set_defaults = mod_simple_vhost_set_defaults;
207 	p->handle_docroot  = mod_simple_vhost_docroot;
208 	p->cleanup     = mod_simple_vhost_free;
209 
210 	return 0;
211 }
212