1 | /* |
---|
2 | * handler.c -- URL handler support |
---|
3 | * |
---|
4 | * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. |
---|
5 | * |
---|
6 | * See the file "license.txt" for usage and redistribution license requirements |
---|
7 | * |
---|
8 | * $Id$ |
---|
9 | */ |
---|
10 | |
---|
11 | /******************************** Description *********************************/ |
---|
12 | |
---|
13 | /* |
---|
14 | * This module implements a URL handler interface and API to permit |
---|
15 | * the addition of user definable URL processors. |
---|
16 | */ |
---|
17 | |
---|
18 | /********************************* Includes ***********************************/ |
---|
19 | |
---|
20 | #include "wsIntrn.h" |
---|
21 | |
---|
22 | /*********************************** Locals ***********************************/ |
---|
23 | |
---|
24 | static websUrlHandlerType *websUrlHandler; /* URL handler list */ |
---|
25 | static int websUrlHandlerMax; /* Number of entries */ |
---|
26 | static int urlHandlerOpenCount = 0; /* count of apps */ |
---|
27 | |
---|
28 | /**************************** Forward Declarations ****************************/ |
---|
29 | |
---|
30 | static int websUrlHandlerSort(const void *p1, const void *p2); |
---|
31 | static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, |
---|
32 | int sid, char_t *url, char_t *path, char_t *query); |
---|
33 | static char_t *websCondenseMultipleChars(char_t *strToCondense, char_t cCondense); |
---|
34 | |
---|
35 | /*********************************** Code *************************************/ |
---|
36 | /* |
---|
37 | * Initialize the URL handler module |
---|
38 | */ |
---|
39 | |
---|
40 | int websUrlHandlerOpen(void) |
---|
41 | { |
---|
42 | if (++urlHandlerOpenCount == 1) { |
---|
43 | websAspOpen(); |
---|
44 | websUrlHandler = NULL; |
---|
45 | websUrlHandlerMax = 0; |
---|
46 | } |
---|
47 | return 0; |
---|
48 | } |
---|
49 | |
---|
50 | /******************************************************************************/ |
---|
51 | /* |
---|
52 | * Close the URL handler module |
---|
53 | */ |
---|
54 | |
---|
55 | void websUrlHandlerClose(void) |
---|
56 | { |
---|
57 | websUrlHandlerType *sp; |
---|
58 | |
---|
59 | if (--urlHandlerOpenCount <= 0) { |
---|
60 | websAspClose(); |
---|
61 | for (sp = websUrlHandler; sp < &websUrlHandler[websUrlHandlerMax]; |
---|
62 | sp++) { |
---|
63 | bfree(B_L, sp->urlPrefix); |
---|
64 | if (sp->webDir) { |
---|
65 | bfree(B_L, sp->webDir); |
---|
66 | } |
---|
67 | } |
---|
68 | bfree(B_L, websUrlHandler); |
---|
69 | websUrlHandlerMax = 0; |
---|
70 | } |
---|
71 | } |
---|
72 | |
---|
73 | /******************************************************************************/ |
---|
74 | /* |
---|
75 | * Define a new URL handler. urlPrefix is the URL prefix to match. webDir is |
---|
76 | * an optional root directory path for a web directory. arg is an optional |
---|
77 | * arg to pass to the URL handler. flags defines the matching order. Valid |
---|
78 | * flags include WEBS_HANDLER_LAST, WEBS_HANDLER_FIRST. If multiple users |
---|
79 | * specify last or first, their order is defined alphabetically by the |
---|
80 | * urlPrefix. |
---|
81 | */ |
---|
82 | |
---|
83 | int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg, |
---|
84 | int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webdir, int arg, |
---|
85 | char_t *url, char_t *path, char_t *query), int flags) |
---|
86 | { |
---|
87 | websUrlHandlerType *sp; |
---|
88 | int len; |
---|
89 | |
---|
90 | a_assert(urlPrefix); |
---|
91 | a_assert(handler); |
---|
92 | |
---|
93 | /* |
---|
94 | * Grow the URL handler array to create a new slot |
---|
95 | */ |
---|
96 | len = (websUrlHandlerMax + 1) * sizeof(websUrlHandlerType); |
---|
97 | if ((websUrlHandler = brealloc(B_L, websUrlHandler, len)) == NULL) { |
---|
98 | return -1; |
---|
99 | } |
---|
100 | sp = &websUrlHandler[websUrlHandlerMax++]; |
---|
101 | memset(sp, 0, sizeof(websUrlHandlerType)); |
---|
102 | |
---|
103 | sp->urlPrefix = bstrdup(B_L, urlPrefix); |
---|
104 | sp->len = gstrlen(sp->urlPrefix); |
---|
105 | if (webDir) { |
---|
106 | sp->webDir = bstrdup(B_L, webDir); |
---|
107 | } else { |
---|
108 | sp->webDir = bstrdup(B_L, T("")); |
---|
109 | } |
---|
110 | sp->handler = handler; |
---|
111 | sp->arg = arg; |
---|
112 | sp->flags = flags; |
---|
113 | |
---|
114 | /* |
---|
115 | * Sort in decreasing URL length order observing the flags for first and last |
---|
116 | */ |
---|
117 | qsort(websUrlHandler, websUrlHandlerMax, sizeof(websUrlHandlerType), |
---|
118 | websUrlHandlerSort); |
---|
119 | return 0; |
---|
120 | } |
---|
121 | |
---|
122 | /******************************************************************************/ |
---|
123 | /* |
---|
124 | * Delete an existing URL handler. We don't reclaim the space of the old |
---|
125 | * handler, just NULL the entry. Return -1 if handler is not found. |
---|
126 | */ |
---|
127 | |
---|
128 | int websUrlHandlerDelete(int (*handler)(webs_t wp, char_t *urlPrefix, |
---|
129 | char_t *webDir, int arg, char_t *url, char_t *path, char_t *query)) |
---|
130 | { |
---|
131 | websUrlHandlerType *sp; |
---|
132 | int i; |
---|
133 | |
---|
134 | for (i = 0; i < websUrlHandlerMax; i++) { |
---|
135 | sp = &websUrlHandler[i]; |
---|
136 | if (sp->handler == handler) { |
---|
137 | sp->handler = NULL; |
---|
138 | return 0; |
---|
139 | } |
---|
140 | } |
---|
141 | return -1; |
---|
142 | } |
---|
143 | |
---|
144 | /******************************************************************************/ |
---|
145 | /* |
---|
146 | * Sort in decreasing URL length order observing the flags for first and last |
---|
147 | */ |
---|
148 | |
---|
149 | static int websUrlHandlerSort(const void *p1, const void *p2) |
---|
150 | { |
---|
151 | websUrlHandlerType *s1, *s2; |
---|
152 | int rc; |
---|
153 | |
---|
154 | a_assert(p1); |
---|
155 | a_assert(p2); |
---|
156 | |
---|
157 | s1 = (websUrlHandlerType*) p1; |
---|
158 | s2 = (websUrlHandlerType*) p2; |
---|
159 | |
---|
160 | if ((s1->flags & WEBS_HANDLER_FIRST) || (s2->flags & WEBS_HANDLER_LAST)) { |
---|
161 | return -1; |
---|
162 | } |
---|
163 | |
---|
164 | if ((s2->flags & WEBS_HANDLER_FIRST) || (s1->flags & WEBS_HANDLER_LAST)) { |
---|
165 | return 1; |
---|
166 | } |
---|
167 | |
---|
168 | if ((rc = gstrcmp(s1->urlPrefix, s2->urlPrefix)) == 0) { |
---|
169 | if (s1->len < s2->len) { |
---|
170 | return 1; |
---|
171 | } else if (s1->len > s2->len) { |
---|
172 | return -1; |
---|
173 | } |
---|
174 | } |
---|
175 | return -rc; |
---|
176 | } |
---|
177 | |
---|
178 | /******************************************************************************/ |
---|
179 | /* |
---|
180 | * Publish a new web directory (Use the default URL handler) |
---|
181 | */ |
---|
182 | |
---|
183 | int websPublish(char_t *urlPrefix, char_t *path) |
---|
184 | { |
---|
185 | return websUrlHandlerDefine(urlPrefix, path, 0, websPublishHandler, 0); |
---|
186 | } |
---|
187 | |
---|
188 | /******************************************************************************/ |
---|
189 | /* |
---|
190 | * Return the directory for a given prefix. Ignore empty prefixes |
---|
191 | */ |
---|
192 | |
---|
193 | char_t *websGetPublishDir(char_t *path, char_t **urlPrefix) |
---|
194 | { |
---|
195 | websUrlHandlerType *sp; |
---|
196 | int i; |
---|
197 | |
---|
198 | for (i = 0; i < websUrlHandlerMax; i++) { |
---|
199 | sp = &websUrlHandler[i]; |
---|
200 | if (sp->urlPrefix[0] == '\0') { |
---|
201 | continue; |
---|
202 | } |
---|
203 | if (sp->handler && gstrncmp(sp->urlPrefix, path, sp->len) == 0) { |
---|
204 | if (urlPrefix) { |
---|
205 | *urlPrefix = sp->urlPrefix; |
---|
206 | } |
---|
207 | return sp->webDir; |
---|
208 | } |
---|
209 | } |
---|
210 | return NULL; |
---|
211 | } |
---|
212 | |
---|
213 | /******************************************************************************/ |
---|
214 | /* |
---|
215 | * Publish URL handler. We just patch the web page Directory and let the |
---|
216 | * default handler do the rest. |
---|
217 | */ |
---|
218 | |
---|
219 | static int websPublishHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, |
---|
220 | int sid, char_t *url, char_t *path, char_t *query) |
---|
221 | { |
---|
222 | int len; |
---|
223 | |
---|
224 | a_assert(websValid(wp)); |
---|
225 | a_assert(path); |
---|
226 | |
---|
227 | /* |
---|
228 | * Trim the urlPrefix off the path and set the webdirectory. Add one to step |
---|
229 | * over the trailing '/' |
---|
230 | */ |
---|
231 | len = gstrlen(urlPrefix) + 1; |
---|
232 | websSetRequestPath(wp, webDir, &path[len]); |
---|
233 | return 0; |
---|
234 | } |
---|
235 | |
---|
236 | /******************************************************************************/ |
---|
237 | /* |
---|
238 | * See if any valid handlers are defined for this request. If so, call them |
---|
239 | * and continue calling valid handlers until one accepts the request. |
---|
240 | * Return true if a handler was invoked, else return FALSE. |
---|
241 | */ |
---|
242 | |
---|
243 | int websUrlHandlerRequest(webs_t wp) |
---|
244 | { |
---|
245 | websUrlHandlerType *sp; |
---|
246 | int i, first; |
---|
247 | |
---|
248 | a_assert(websValid(wp)); |
---|
249 | |
---|
250 | /* |
---|
251 | * Delete the socket handler as we don't want to start reading any |
---|
252 | * data on the connection as it may be for the next pipelined HTTP/1.1 |
---|
253 | * request if using Keep Alive |
---|
254 | */ |
---|
255 | socketDeleteHandler(wp->sid); |
---|
256 | wp->state = WEBS_PROCESSING; |
---|
257 | websStats.handlerHits++; |
---|
258 | |
---|
259 | websSetRequestPath(wp, websGetDefaultDir(), NULL); |
---|
260 | |
---|
261 | /* |
---|
262 | * Eliminate security hole |
---|
263 | */ |
---|
264 | websCondenseMultipleChars(wp->path, '/'); |
---|
265 | websCondenseMultipleChars(wp->url, '/'); |
---|
266 | |
---|
267 | /* |
---|
268 | * We loop over each handler in order till one accepts the request. |
---|
269 | * The security handler will handle the request if access is NOT allowed. |
---|
270 | */ |
---|
271 | first = 1; |
---|
272 | for (i = 0; i < websUrlHandlerMax; i++) { |
---|
273 | sp = &websUrlHandler[i]; |
---|
274 | if (sp->handler && gstrncmp(sp->urlPrefix, wp->path, sp->len) == 0) { |
---|
275 | if (first) { |
---|
276 | websSetEnv(wp); |
---|
277 | first = 0; |
---|
278 | } |
---|
279 | if ((*sp->handler)(wp, sp->urlPrefix, sp->webDir, sp->arg, |
---|
280 | wp->url, wp->path, wp->query)) { |
---|
281 | return 1; |
---|
282 | } |
---|
283 | if (!websValid(wp)) { |
---|
284 | trace(0, |
---|
285 | T("webs: handler %s called websDone, but didn't return 1\n"), |
---|
286 | sp->urlPrefix); |
---|
287 | return 1; |
---|
288 | } |
---|
289 | } |
---|
290 | } |
---|
291 | /* |
---|
292 | * If no handler processed the request, then return an error. Note: It is |
---|
293 | * the handlers responsibility to call websDone |
---|
294 | */ |
---|
295 | if (i >= websUrlHandlerMax) { |
---|
296 | websError(wp, 200, T("No handler for this URL %s"), wp->url); |
---|
297 | } |
---|
298 | return 0; |
---|
299 | } |
---|
300 | |
---|
301 | #ifdef OBSOLETE_CODE |
---|
302 | |
---|
303 | /******************************************************************************/ |
---|
304 | /* |
---|
305 | * Tidy up the URL path. Return -1 if the URL is bad. |
---|
306 | * Used to eliminate repeated directory delimiters ('/'). |
---|
307 | */ |
---|
308 | |
---|
309 | static int websTidyUrl(webs_t wp) |
---|
310 | { |
---|
311 | char_t *parts[64]; /* Array of ptr's to URL parts */ |
---|
312 | char_t *token, *url, *tidyurl; |
---|
313 | int i, len, npart; |
---|
314 | |
---|
315 | a_assert(websValid(wp)); |
---|
316 | |
---|
317 | /* |
---|
318 | * Copy the string so we don't destroy the original (yet) |
---|
319 | */ |
---|
320 | url = bstrdup(B_L, wp->url); |
---|
321 | websDecodeUrl(url, url, gstrlen(url)); |
---|
322 | |
---|
323 | len = npart = 0; |
---|
324 | parts[0] = NULL; |
---|
325 | token = gstrtok(url, T("/")); |
---|
326 | |
---|
327 | /* |
---|
328 | * Look at each directory segment and process "." and ".." segments |
---|
329 | * Don't allow the browser to pop outside the root web. |
---|
330 | */ |
---|
331 | while (token != NULL) { |
---|
332 | if (gstrcmp(token, T("..")) == 0) { |
---|
333 | if (npart > 0) { |
---|
334 | npart--; |
---|
335 | } |
---|
336 | |
---|
337 | } else if (gstrcmp(token, T(".")) != 0) { |
---|
338 | parts[npart] = token; |
---|
339 | len += gstrlen(token) + 1; |
---|
340 | npart++; |
---|
341 | } |
---|
342 | token = gstrtok(NULL, T("/")); |
---|
343 | } |
---|
344 | |
---|
345 | /* |
---|
346 | * Re-construct URL. Need extra space all "/" and null. |
---|
347 | */ |
---|
348 | if (npart || (gstrcmp(url, T("/")) == 0) || (url[0] == '\0')) { |
---|
349 | tidyurl = balloc(B_L, (len + 2) * sizeof(char_t)); |
---|
350 | *tidyurl = '\0'; |
---|
351 | |
---|
352 | for (i = 0; i < npart; i++) { |
---|
353 | gstrcat(tidyurl, T("/")); |
---|
354 | gstrcat(tidyurl, parts[i]); |
---|
355 | } |
---|
356 | |
---|
357 | bfree(B_L, url); |
---|
358 | |
---|
359 | bfree(B_L, wp->url); |
---|
360 | wp->url = tidyurl; |
---|
361 | return 0; |
---|
362 | } else { |
---|
363 | bfree(B_L, url); |
---|
364 | return -1; |
---|
365 | } |
---|
366 | } |
---|
367 | |
---|
368 | #endif |
---|
369 | |
---|
370 | /******************************************************************************/ |
---|
371 | /* |
---|
372 | * Convert multiple adjacent occurrences of a given character to a single |
---|
373 | * instance. |
---|
374 | */ |
---|
375 | |
---|
376 | static char_t *websCondenseMultipleChars(char_t *strToCondense, char_t cCondense) |
---|
377 | { |
---|
378 | if (strToCondense != NULL) { |
---|
379 | char_t *pStr, *pScan; |
---|
380 | |
---|
381 | pStr = pScan = strToCondense; |
---|
382 | |
---|
383 | while (*pScan && *pStr) { |
---|
384 | /* |
---|
385 | * Advance scan pointer over multiple occurences of condense character |
---|
386 | */ |
---|
387 | while ((*pScan == cCondense) && (*(pScan + 1) == cCondense)) { |
---|
388 | pScan++; |
---|
389 | } |
---|
390 | /* |
---|
391 | * Copy character if an advance of the scan pointer has occurred |
---|
392 | */ |
---|
393 | if (pStr != pScan) { |
---|
394 | *pStr = *pScan; |
---|
395 | } |
---|
396 | |
---|
397 | pScan++; |
---|
398 | pStr++; |
---|
399 | } |
---|
400 | /* |
---|
401 | * Zero terminate string if multiple adjacent characters were found and condensed |
---|
402 | */ |
---|
403 | if (pStr != pScan) { |
---|
404 | *pStr = 0; |
---|
405 | } |
---|
406 | } |
---|
407 | |
---|
408 | return strToCondense; |
---|
409 | } |
---|
410 | |
---|
411 | /******************************************************************************/ |
---|