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