1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "bsearch-insert-pos.h"
6 #include "str-sanitize.h"
7 
8 #include "http-url.h"
9 #include "http-server-private.h"
10 
11 static struct event_category event_category_http_server_resource = {
12 	.name = "http-server-resource"
13 };
14 
15 /*
16  * Location
17  */
18 
19 static int
http_server_location_cmp(struct http_server_location * const * loc1,struct http_server_location * const * loc2)20 http_server_location_cmp(struct http_server_location *const *loc1,
21 			 struct http_server_location *const *loc2)
22 {
23 	return strcmp((*loc1)->path, (*loc2)->path);
24 }
25 
26 static struct http_server_location *
http_server_location_add(struct http_server * server,pool_t pool,const char * path)27 http_server_location_add(struct http_server *server, pool_t pool,
28 			 const char *path)
29 {
30 	struct http_server_location qloc, *loc;
31 	unsigned int insert_idx;
32 
33 	i_zero(&qloc);
34 	qloc.path = path;
35 	loc = &qloc;
36 
37 	if (array_bsearch_insert_pos(&server->locations, &loc,
38 				     http_server_location_cmp, &insert_idx))
39 		return array_idx_elem(&server->locations, insert_idx);
40 
41 	loc = p_new(pool, struct http_server_location, 1);
42 	loc->path = p_strdup(pool, path);
43 	array_insert(&server->locations, insert_idx, &loc, 1);
44 	return loc;
45 }
46 
47 static int
http_server_location_find(struct http_server * server,const char * path,struct http_server_location ** loc_r,const char ** sub_path_r)48 http_server_location_find(struct http_server *server, const char *path,
49 			  struct http_server_location **loc_r,
50 			  const char **sub_path_r)
51 {
52 	struct http_server_location qloc, *loc;
53 	size_t loc_len;
54 	unsigned int insert_idx;
55 
56 	*sub_path_r = NULL;
57 	*loc_r = NULL;
58 
59 	i_zero(&qloc);
60 	qloc.path = path;
61 	loc = &qloc;
62 
63 	if (array_bsearch_insert_pos(&server->locations, &loc,
64 				     http_server_location_cmp, &insert_idx)) {
65 		/* Exact match */
66 		*loc_r = array_idx_elem(&server->locations, insert_idx);
67 		*sub_path_r = "";
68 		return 1;
69 	}
70 	if (insert_idx == 0) {
71 		/* Not found at all */
72 		return -1;
73 	}
74 	loc = array_idx_elem(&server->locations, insert_idx-1);
75 
76 	loc_len = strlen(loc->path);
77 	if (!str_begins(path, loc->path)) {
78 		/* Location isn't a prefix of path */
79 		return -1;
80 	} else if (path[loc_len] != '/') {
81 		/* Match doesn't end at '/' */
82 		return -1;
83 	}
84 
85 	*sub_path_r = &path[loc_len + 1];
86 	*loc_r = loc;
87 	return 0;
88 }
89 
90 static void
http_server_location_remove(struct http_server * server,struct http_server_location * loc)91 http_server_location_remove(struct http_server *server,
92 			    struct http_server_location *loc)
93 {
94 	struct http_server_location *const *locp;
95 
96 	array_foreach(&server->locations, locp) {
97 		if (*locp == loc) {
98 			array_delete(
99 				&server->locations,
100 				array_foreach_idx(&server->locations, locp), 1);
101 			return;
102 		}
103 	}
104 }
105 
106 /*
107  * Resource
108  */
109 
http_server_resource_update_event(struct http_server_resource * res)110 static void http_server_resource_update_event(struct http_server_resource *res)
111 {
112 	struct http_server_location *const *locs;
113 	unsigned int locs_count;
114 
115 	locs = array_get(&res->locations, &locs_count);
116 	if (locs_count == 0) {
117 		event_set_append_log_prefix(res->event, "resource: ");
118 		return;
119 	}
120 
121 	event_add_str(res->event, "path", locs[0]->path);
122 	event_set_append_log_prefix(
123 		res->event, t_strdup_printf("resource %s: ",
124 			str_sanitize(locs[0]->path, 128)));
125 }
126 
127 #undef http_server_resource_create
128 struct http_server_resource *
http_server_resource_create(struct http_server * server,pool_t pool,http_server_resource_callback_t * callback,void * context)129 http_server_resource_create(struct http_server *server, pool_t pool,
130 			    http_server_resource_callback_t *callback,
131 			    void *context)
132 {
133 	struct http_server_resource *res;
134 
135 	pool_ref(pool);
136 
137 	pool = pool_alloconly_create("http server resource", 1024);
138 	res = p_new(pool, struct http_server_resource, 1);
139 	res->pool = pool;
140 	res->server = server;
141 
142 	res->callback = callback;
143 	res->context = context;
144 
145 	p_array_init(&res->locations, pool, 4);
146 
147 	res->event = event_create(server->event);
148 	event_add_category(res->event, &event_category_http_server_resource);
149 	http_server_resource_update_event(res);
150 
151 	array_append(&server->resources, &res, 1);
152 
153 	return res;
154 }
155 
http_server_resource_free(struct http_server_resource ** _res)156 void http_server_resource_free(struct http_server_resource **_res)
157 {
158 	struct http_server_resource *res = *_res;
159 	struct http_server_location *loc;
160 
161 	if (res == NULL)
162 		return;
163 
164 	*_res = NULL;
165 
166 	e_debug(res->event, "Free");
167 
168 	if (res->destroy_callback != NULL) {
169 		res->destroy_callback(res->destroy_context);
170 		res->destroy_callback = NULL;
171 	}
172 
173 	array_foreach_elem(&res->locations, loc)
174 		http_server_location_remove(res->server, loc);
175 
176 	event_unref(&res->event);
177 	pool_unref(&res->pool);
178 }
179 
http_server_resource_get_pool(struct http_server_resource * res)180 pool_t http_server_resource_get_pool(struct http_server_resource *res)
181 {
182 	return res->pool;
183 }
184 
http_server_resource_get_path(struct http_server_resource * res)185 const char *http_server_resource_get_path(struct http_server_resource *res)
186 {
187 	struct http_server_location *const *locs;
188 	unsigned int locs_count;
189 
190 	locs = array_get(&res->locations, &locs_count);
191 	i_assert(locs_count > 0);
192 
193 	return locs[0]->path;
194 }
195 
http_server_resource_get_event(struct http_server_resource * res)196 struct event *http_server_resource_get_event(struct http_server_resource *res)
197 {
198 	return res->event;
199 }
200 
http_server_resource_add_location(struct http_server_resource * res,const char * path)201 void http_server_resource_add_location(struct http_server_resource *res,
202 				       const char *path)
203 {
204 	struct http_server_location *loc;
205 
206 	i_assert(*path == '\0' || *path == '/');
207 
208 	loc = http_server_location_add(res->server, res->pool, path);
209 	i_assert(loc->resource == NULL);
210 
211 	loc->resource = res;
212 	array_append(&res->locations, &loc, 1);
213 
214 	if (array_count(&res->locations) == 1)
215 		http_server_resource_update_event(res);
216 }
217 
http_server_resource_find(struct http_server * server,const char * path,struct http_server_resource ** res_r,const char ** sub_path_r)218 int http_server_resource_find(struct http_server *server, const char *path,
219 			      struct http_server_resource **res_r,
220 			      const char **sub_path_r)
221 {
222 	struct http_server_location *loc;
223 	int ret;
224 
225 	if (path == NULL)
226 		return -1;
227 
228 	*res_r = NULL;
229 	*sub_path_r = NULL;
230 
231 	ret = http_server_location_find(server, path, &loc, sub_path_r);
232 	if (ret < 0)
233 		return -1;
234 
235 	i_assert(loc->resource != NULL);
236 	*res_r = loc->resource;
237 	return ret;
238 }
239 
http_server_resource_callback(struct http_server_request * req)240 bool http_server_resource_callback(struct http_server_request *req)
241 {
242 	struct http_server *server = req->server;
243 	struct http_server_resource *res;
244 	const char *sub_path;
245 
246 	switch (req->req.target.format) {
247 	case HTTP_REQUEST_TARGET_FORMAT_ORIGIN:
248 		/* According to RFC 7240, Section 5.3.1 only the origin form is
249 		   applicable to local resources on an origin server.
250 		*/
251 		break;
252 	case HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE:
253 	case HTTP_REQUEST_TARGET_FORMAT_AUTHORITY:
254 	case HTTP_REQUEST_TARGET_FORMAT_ASTERISK:
255 		/* Not applicable for a local resource. */
256 		return FALSE;
257 	}
258 
259 	if (http_server_resource_find(server, req->req.target.url->path,
260 				      &res, &sub_path) < 0)
261 		return FALSE;
262 
263 	e_debug(res->event, "Got request: %s", http_server_request_label(req));
264 
265 	i_assert(res->callback != NULL);
266 	res->callback(res->context, req, sub_path);
267 	return TRUE;
268 }
269 
270 #undef http_server_resource_set_destroy_callback
http_server_resource_set_destroy_callback(struct http_server_resource * res,void (* callback)(void *),void * context)271 void http_server_resource_set_destroy_callback(struct http_server_resource *res,
272 					       void (*callback)(void *),
273 					       void *context)
274 {
275 	res->destroy_callback = callback;
276 	res->destroy_context = context;
277 }
278