1 /* ----------------------------------------------------------------------------
2 * dispatch.c -- implement web::dispatch for websh3
3 * nca-073-9
4 *
5 * Copyright (c) 1996-2000 by Netcetera AG.
6 * Copyright (c) 2001 by Apache Software Foundation.
7 * All rights reserved.
8 *
9 * See the file "license.terms" for information on usage and
10 * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * @(#) $Id: dispatch.c 814683 2009-09-14 15:11:40Z ronnie $
13 * ------------------------------------------------------------------------- */
14
15 #include "args.h"
16 #include "log.h"
17 #include "paramlist.h"
18 #include "request.h"
19 #include "tcl.h"
20 #include "webutl.h"
21
22 #define FORM_URLENCODED "application/x-www-form-urlencoded"
23 #define FORM_MULTIPART "multipart/form-data"
24 #define FORM_MULTIPART_LEN 19
25 #define FORM_DEFAULT_TYPE FORM_URLENCODED
26
27 int parsePostData(Tcl_Interp * interp, Tcl_Obj * name,
28 Tcl_Obj * type, Tcl_Obj * len, RequestData * requestData);
29
30
31 /* ----------------------------------------------------------------------------
32 * Web_Dispatch
33 * ------------------------------------------------------------------------- */
Web_Dispatch(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])34 int Web_Dispatch(ClientData clientData,
35 Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
36 {
37
38 RequestData *requestData = NULL;
39 TCLCONST char *params[] = { "-track",
40 "-querystring",
41 "-postdata",
42 "-cmd",
43 "-hook",
44 NULL
45 };
46 enum params
47 { TRACK,
48 QUERYSTRING,
49 POSTDATA,
50 CMD,
51 HOOK
52 };
53
54 int idx = 0;
55 Tcl_Obj *post_data;
56 Tcl_Obj *query_string;
57
58 /* --------------------------------------------------------------------------
59 * check for private data
60 * ----------------------------------------------------------------------- */
61 WebAssertData(interp, clientData, "Web_Dispatch", TCL_ERROR)
62 requestData = (RequestData *) clientData;
63
64 /* --------------------------------------------------------------------------
65 * check for not-accepted options
66 * ----------------------------------------------------------------------- */
67 WebAssertArgs(interp, objc, objv, params, idx, -1);
68
69 /* make sure we have values */
70 if (requestFillRequestValues(interp, requestData) == TCL_ERROR)
71 return TCL_ERROR;
72
73
74 /* ==========================================================================
75 * query_string
76 * ======================================================================= */
77 query_string = argValueOfKey(objc, objv, (char *)params[QUERYSTRING]);
78
79 if (query_string == NULL) {
80 /* ------------------------------------------------------------------------
81 * not on command line. try to get from response object
82 * --------------------------------------------------------------------- */
83 query_string =
84 paramListGetObjectByString(interp, requestData->request,
85 "QUERY_STRING");
86 }
87
88 /* --------------------------------------------------------------------------
89 * did we find any query_string ?
90 * ----------------------------------------------------------------------- */
91 if (query_string != NULL) {
92
93 Tcl_IncrRefCount(query_string);
94
95 /* ------------------------------------------------------------------------
96 * empty string means: skip
97 * --------------------------------------------------------------------- */
98 if (Tcl_GetCharLength(query_string) > 0) {
99
100 if (parseQueryString(requestData, interp, query_string) ==
101 TCL_ERROR) {
102 Tcl_DecrRefCount(query_string);
103 return TCL_ERROR;
104 }
105 }
106 Tcl_DecrRefCount(query_string);
107 }
108
109 /* ==========================================================================
110 * post_data
111 * ======================================================================= */
112 post_data = argValueOfKey(objc, objv, (char *)params[POSTDATA]);
113
114 if (post_data != NULL) {
115
116 /* ------------------------------------------------------------------------
117 * on command line; empty string means: skip
118 * --------------------------------------------------------------------- */
119 if (Tcl_GetCharLength(post_data) > 0) {
120
121 /* ----------------------------------------------------------------------
122 * parse -postdata input
123 * ------------------------------------------------------------------- */
124 int idx1 = 0;
125 int idx2 = 0;
126
127 if ((idx1 = argIndexOfKey(objc, objv, (char *)params[POSTDATA])) > 0) {
128
129 idx2 = argIndexOfNextKey(objc, objv, idx1);
130
131 /* for( i = idx1; i < objc; i++) { */
132 /* fprintf(stdout,"DBG %d (%d) %s\n",i,(i == idx2),Tcl_GetString(objv[i])); fflush(stdout); */
133 /* } */
134
135 switch ((idx2 - idx1)) {
136
137 case 2:
138 /* ------------------------------------------------------------------
139 * -postdata channel
140 * --------------------------------------------------------------- */
141 if (parsePostData(interp, objv[idx1 + 1], NULL, NULL,
142 requestData) == TCL_ERROR) {
143 return TCL_ERROR;
144 }
145
146 break;
147 case 3:
148 /* ------------------------------------------------------------------
149 * -postdata channel length
150 * --------------------------------------------------------------- */
151 /* log is handled by parsePostData */
152 if (parsePostData(interp, objv[idx1 + 1], objv[idx1 + 2],
153 NULL, requestData) == TCL_ERROR) {
154 return TCL_ERROR;
155 }
156 break;
157 case 4:
158 /* ------------------------------------------------------------------
159 * -postdata channel length type
160 * --------------------------------------------------------------- */
161 /* log is handled by parsePostData */
162 if (parsePostData(interp, objv[idx1 + 1], objv[idx1 + 2],
163 objv[idx1 + 3], requestData) == TCL_ERROR) {
164 return TCL_ERROR;
165 }
166 break;
167 default:
168 Tcl_WrongNumArgs(interp, 1, objv,
169 "-postdata ?#?channel ?content_length? ?content_type?");
170 return TCL_ERROR;
171 break;
172 }
173 }
174 }
175 }
176 else {
177
178 /* ------------------------------------------------------------------------
179 * get postdata from default input
180 * --------------------------------------------------------------------- */
181
182 Tcl_Obj *content_type = NULL;
183 Tcl_Obj *content_length = NULL;
184
185 content_type =
186 paramListGetObjectByString(interp, requestData->request,
187 "CONTENT_TYPE");
188 content_length =
189 paramListGetObjectByString(interp, requestData->request,
190 "CONTENT_LENGTH");
191
192 if ((content_type != NULL) && (content_length != NULL)) {
193
194 Tcl_Obj *tmp = NULL;
195 Tcl_IncrRefCount(content_type);
196 Tcl_IncrRefCount(content_length);
197
198 tmp = requestGetDefaultChannelName(interp);
199
200 Tcl_IncrRefCount(tmp);
201
202 parsePostData(interp, tmp, content_length, content_type,
203 requestData);
204
205 Tcl_DecrRefCount(tmp);
206 }
207 WebDecrRefCountIfNotNull(content_type);
208 WebDecrRefCountIfNotNull(content_length);
209 }
210
211 /* ==========================================================================
212 * track
213 * ======================================================================= */
214 {
215 Tcl_Obj *trackList = NULL;
216 int listLen = -1;
217 int i = -1;
218 Tcl_Obj *key = NULL;
219 Tcl_Obj *val = NULL;
220
221 trackList = argValueOfKey(objc, objv, (char *)params[TRACK]);
222
223 if ((trackList != NULL) &&
224 (listLen = tclGetListLength(interp, trackList)) != -1) {
225
226 /* ----------------------------------------------------------------------
227 * loop over list and add these parameters to "static"
228 * ------------------------------------------------------------------- */
229
230 for (i = 0; i < listLen; i++) {
231
232 key = NULL;
233 val = NULL;
234
235 Tcl_ListObjIndex(interp, trackList, i, &key);
236
237 /* Tcl_ListObjIndex: The reference count for the list element
238 * is not incremented; the caller must do that if it needs to
239 * retain a pointer to the element. */
240
241 if (key != NULL) {
242
243 /* ------------------------------------------------------------------
244 * get value from params
245 * --------------------------------------------------------------- */
246 val = (Tcl_Obj *) getFromHashTable(requestData->paramList,
247 Tcl_GetString(key));
248
249 if (val != NULL) {
250
251 /* this obj now belongs to two lists */
252 val = Tcl_DuplicateObj(val);
253
254 if (paramListSetAsWhole(requestData->staticList,
255 Tcl_GetString(key),
256 val) == TCL_ERROR) {
257 LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__,
258 "web::dispatch -track", WEBLOG_INFO,
259 "error adding \"", Tcl_GetString(key),
260 ", ", Tcl_GetString(val),
261 "\" to static params", NULL);
262 }
263 }
264 }
265 }
266 }
267 }
268
269 /* ==========================================================================
270 * dispatch
271 * ======================================================================= */
272 {
273
274 Tcl_Obj *cmdName = NULL;
275 Tcl_Obj *cmdCode = NULL;
276 char *cmdNameStr = NULL;
277 int res;
278
279 /* ------------------------------------------------------------------------
280 * reset interp result
281 * --------------------------------------------------------------------- */
282 Tcl_ResetResult(interp);
283
284 cmdName = argValueOfKey(objc, objv, (char *)params[CMD]);
285
286 if (cmdName != NULL) {
287
288 /* ----------------------------------------------------------------------
289 * call command (from command-line); empty string means: no dispatch
290 * ------------------------------------------------------------------- */
291 if (Tcl_GetCharLength(cmdName) < 1)
292 return TCL_OK;
293
294 }
295 else {
296
297 /* ----------------------------------------------------------------------
298 * get command name from default source
299 * ------------------------------------------------------------------- */
300 cmdName = (Tcl_Obj *) getFromHashTable(requestData->paramList,
301 Tcl_GetString(requestData->
302 cmdTag));
303 }
304
305 /* ------------------------------------------------------------------------
306 * cmd: "",NULL --> default
307 * --------------------------------------------------------------------- */
308 if (cmdName == NULL) {
309 cmdName = Tcl_NewStringObj(WEB_REQ_DEFAULT, -1);
310 }
311 else if (Tcl_GetCharLength(cmdName) == 0) {
312 cmdName = Tcl_NewStringObj(WEB_REQ_DEFAULT, -1);
313 }
314 else {
315 cmdName = Tcl_DuplicateObj(cmdName);
316 }
317 cmdNameStr = Tcl_GetString(cmdName);
318
319 LOG_MSG(interp, WRITE_LOG,
320 __FILE__, __LINE__,
321 "web::dispatch", WEBLOG_INFO,
322 "Handling command \"", cmdNameStr, "\"", NULL);
323
324 /* ------------------------------------------------------------------------
325 * get command code
326 * --------------------------------------------------------------------- */
327 cmdCode =
328 (Tcl_Obj *) getFromHashTable(requestData->cmdList, cmdNameStr);
329
330 /* ------------------------------------------------------------------------
331 * command found ?
332 * --------------------------------------------------------------------- */
333 if (cmdCode == NULL) {
334
335 LOG_MSG(interp, WRITE_LOG,
336 __FILE__, __LINE__,
337 "web::dispatch", WEBLOG_INFO,
338 "command \"", cmdNameStr, "\" not found.",
339 " Switching to command \"default\"", NULL);
340
341 cmdNameStr = WEB_REQ_DEFAULT;
342 cmdCode =
343 (Tcl_Obj *) getFromHashTable(requestData->cmdList,
344 cmdNameStr);
345 }
346
347 /* ------------------------------------------------------------------------
348 * eval command, if any
349 * --------------------------------------------------------------------- */
350 if (cmdCode == NULL) {
351
352 LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
353 "web::dispatch", WEBLOG_ERROR,
354 "command \"", cmdNameStr, "\" not found", NULL);
355
356 WebDecrRefCountIfNotNull(cmdName);
357 return TCL_ERROR;
358
359 }
360 else {
361
362 Tcl_Obj *hook = NULL;
363
364 /* first eval hook, if any */
365 hook = argValueOfKey(objc, objv, (char *)params[HOOK]);
366
367 if (hook != NULL) {
368
369 Tcl_IncrRefCount(hook);
370 res = Tcl_EvalObjEx(interp, hook, TCL_EVAL_DIRECT);
371 Tcl_DecrRefCount(hook);
372
373 if (res == TCL_ERROR) {
374 LOG_MSG(interp, WRITE_LOG | INTERP_ERRORINFO,
375 __FILE__, __LINE__,
376 "web::dispatch", WEBLOG_ERROR,
377 "error evaluating hook \"", Tcl_GetString(hook),
378 "\"", NULL);
379 WebDecrRefCountIfNotNull(cmdName);
380 return TCL_ERROR;
381 }
382 }
383
384 /* reuse var hook */
385 Tcl_ListObjIndex(interp, cmdCode, 0, &hook);
386
387 Tcl_IncrRefCount(hook);
388 res = Tcl_EvalObjEx(interp, hook, TCL_EVAL_DIRECT);
389 Tcl_DecrRefCount(hook);
390
391 if (res == TCL_ERROR) {
392
393 LOG_MSG(interp, WRITE_LOG | SET_RESULT | INTERP_ERRORINFO,
394 __FILE__, __LINE__,
395 "web::dispatch", WEBLOG_ERROR,
396 "error evaluating command \"", cmdNameStr,
397 "\"", NULL);
398 WebDecrRefCountIfNotNull(cmdName);
399 return TCL_ERROR;
400
401 }
402 }
403 WebDecrRefCountIfNotNull(cmdName);
404 }
405
406 return TCL_OK;
407 }
408
409
410 /* ----------------------------------------------------------------------------
411 * parsePostData -- parse postdata from a channel
412 * ------------------------------------------------------------------------- */
parsePostData(Tcl_Interp * interp,Tcl_Obj * name,Tcl_Obj * len,Tcl_Obj * type,RequestData * requestData)413 int parsePostData(Tcl_Interp * interp, Tcl_Obj * name,
414 Tcl_Obj * len, Tcl_Obj * type, RequestData * requestData)
415 {
416
417 char *content_type;
418
419 /* printf("DBG parsePostData - starting\n"); fflush(stdout); */
420
421 /* --------------------------------------------------------------------------
422 * input checking
423 * ----------------------------------------------------------------------- */
424 if (name == NULL) {
425 LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
426 "we;::dispatch -postdata", WEBLOG_ERROR,
427 "cannot access channelName", NULL);
428 return TCL_ERROR;
429 }
430
431 if (requestData == NULL) {
432 LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
433 "we;::dispatch -postdata", WEBLOG_ERROR,
434 "cannot access internal data", NULL);
435 return TCL_ERROR;
436 }
437
438 if (type == NULL)
439 content_type = FORM_DEFAULT_TYPE;
440 else
441 content_type = Tcl_GetString(type);
442
443 /* printf("DBG parsePostData - content_type: %s\n",content_type); fflush(stdout); */
444
445 /* --------------------------------------------------------------------------
446 * application/x-www-form-urlencoded
447 * ----------------------------------------------------------------------- */
448 if (strcmp(content_type, FORM_URLENCODED) == 0) {
449
450 return parseUrlEncodedFormData(requestData, interp,
451 Tcl_GetString(name), len);
452 }
453 /* ------------------------------------------------------------------------
454 * multipart/form-data
455 * --------------------------------------------------------------------- */
456 if (strncmp(content_type, FORM_MULTIPART, FORM_MULTIPART_LEN) == 0) {
457
458 return parseMultipartFormData(requestData, interp,
459 Tcl_GetString(name), content_type);
460 }
461
462 LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__,
463 "web::dispatch -postdata", WEBLOG_WARNING,
464 "unknown content-type \"", content_type, "\"", NULL);
465 return TCL_ERROR;
466 }
467