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