1 /*
2  * request.c -- request data management
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: request.c 814683 2009-09-14 15:11:40Z ronnie $
13  *
14  */
15 #include <tcl.h>
16 #include <time.h>
17 #include "request.h"
18 #include "paramlist.h"
19 #include "crypt.h"
20 #include <stdio.h>
21 #include "log.h"
22 
23 #ifdef WIN32
24 #include <errno.h>
25 #else
26 #include <sys/errno.h>
27 #endif
28 
destroyRequestDataHook(ClientData clientData)29 void destroyRequestDataHook(ClientData clientData)
30 {
31     destroyRequestData(clientData, NULL);
32 }
33 
34 /* ----------------------------------------------------------------------------
35  * Init --
36  * ------------------------------------------------------------------------- */
request_Init(Tcl_Interp * interp)37 int request_Init(Tcl_Interp * interp)
38 {
39 
40     RequestData *requestData;
41 
42     /* --------------------------------------------------------------------------
43      * interpreter running ?
44      * ----------------------------------------------------------------------- */
45     if (interp == NULL)
46 	return TCL_ERROR;
47 
48     /* --------------------------------------------------------------------------
49      * new data
50      * ----------------------------------------------------------------------- */
51     requestData = createRequestData(interp);
52     WebAssertData(interp, requestData, "request", TCL_ERROR);
53 
54     /* --------------------------------------------------------------------------
55      * register commands
56      * ----------------------------------------------------------------------- */
57 
58     Tcl_CreateObjCommand(interp, "web::request",
59 			 Web_Request, (ClientData) requestData, NULL);
60 
61     Tcl_CreateObjCommand(interp, "web::param",
62 			 Web_Param, (ClientData) requestData, NULL);
63 
64     Tcl_CreateObjCommand(interp, "web::formvar",
65 			 Web_FormVar, (ClientData) requestData, NULL);
66 
67     Tcl_CreateObjCommand(interp, "web::tempfile",
68 			 Web_TempFile, (ClientData) requestData, NULL);
69 
70     Tcl_CreateObjCommand(interp, "web::command",
71 			 Web_Command, (ClientData) requestData, NULL);
72 
73     Tcl_CreateObjCommand(interp, "web::getcommand",
74 			 Web_GetCommand, (ClientData) requestData, NULL);
75 
76     Tcl_CreateObjCommand(interp, "web::dispatch",
77 			 Web_Dispatch, (ClientData) requestData, NULL);
78 
79     /* -------------------------------------------------------------------------
80      * associate data with Interpreter
81      * ---------------------------------------------------------------------- */
82     Tcl_SetAssocData(interp, WEB_REQ_ASSOC_DATA,
83 		     destroyRequestData, (ClientData) requestData);
84 
85     /* -------------------------------------------------------------------------
86      * we need an exit handler (if this is the main interp)
87      * because we need to delete temp files on regular exit too
88      * ---------------------------------------------------------------------- */
89     if (Tcl_GetMaster(interp) == NULL) {
90       Tcl_CreateExitHandler(destroyRequestDataHook, (ClientData) requestData);
91     }
92 
93     /* -------------------------------------------------------------------------
94      * done
95      * ---------------------------------------------------------------------- */
96     return TCL_OK;
97 }
98 
99 
100 /* ----------------------------------------------------------------------------
101  * create
102  * ------------------------------------------------------------------------- */
createRequestData(Tcl_Interp * interp)103 RequestData *createRequestData(Tcl_Interp * interp)
104 {
105 
106     RequestData *requestData = NULL;
107 
108     requestData = WebAllocInternalData(RequestData);
109 
110     if (requestData != NULL) {
111 
112 	WebNewStringObjFromStringIncr(requestData->cmdTag, CMDTAGDEFAULT);
113 	WebNewStringObjFromStringIncr(requestData->timeTag, TIMETAGDEFAULT);
114 	requestData->cmdUrlTimestamp = Tcl_NewBooleanObj(1);
115 	Tcl_IncrRefCount(requestData->cmdUrlTimestamp);
116 	HashUtlAllocInit(requestData->request, TCL_STRING_KEYS);
117 
118 	requestData->upLoadFileSize = Tcl_NewLongObj(UPLOADFILESIZEDEFAULT);
119 	Tcl_IncrRefCount(requestData->upLoadFileSize);
120 	requestData->filePermissions = DEFAULT_FILEPERMISSIONS;
121 
122 	HashUtlAllocInit(requestData->paramList, TCL_STRING_KEYS);
123 	HashUtlAllocInit(requestData->formVarList, TCL_STRING_KEYS);
124 	HashUtlAllocInit(requestData->cmdList, TCL_STRING_KEYS);
125 	HashUtlAllocInit(requestData->tmpFnList, TCL_STRING_KEYS);
126 	HashUtlAllocInit(requestData->staticList, TCL_STRING_KEYS);
127 	requestData->requestIsInitialized = 0;
128     }
129 
130     return requestData;
131 }
132 
133 /* ----------------------------------------------------------------------------
134  * reset
135  * ------------------------------------------------------------------------- */
resetRequestData(Tcl_Interp * interp,RequestData * requestData)136 int resetRequestData(Tcl_Interp * interp, RequestData * requestData)
137 {
138 
139     if ((interp == NULL) || (requestData == NULL))
140 	return TCL_ERROR;
141 
142     if (removeTempFiles(interp, requestData) != TCL_OK)
143 	return TCL_ERROR;
144 
145     if (resetHashTableWithContent(requestData->staticList,
146 				  TCL_STRING_KEYS,
147 				  deleteTclObj_fnc, NULL) != TCL_OK)
148 	return TCL_ERROR;
149 
150     /* do not touch cmdList */
151 
152     if (resetHashTableWithContent(requestData->formVarList,
153 				  TCL_STRING_KEYS,
154 				  deleteTclObj_fnc, NULL) != TCL_OK)
155 	return TCL_ERROR;
156 
157     if (resetHashTableWithContent(requestData->paramList, TCL_STRING_KEYS,
158 				  deleteTclObj_fnc, NULL) != TCL_OK)
159 	return TCL_ERROR;
160 
161     if (resetHashTableWithContent(requestData->request, TCL_STRING_KEYS,
162 				  deleteTclObj_fnc, NULL) != TCL_OK)
163 	return TCL_ERROR;
164 
165 #if 0
166     WebDecrRefCountIfNotNullAndSetNull(requestData->upLoadFileSize);
167     requestData->upLoadFileSize = Tcl_NewLongObj(0);
168     Tcl_IncrRefCount(requestData->upLoadFileSize);
169 
170     requestData->filePermissions = DEFAULT_FILEPERMISSIONS;
171 
172     WebDecrRefCountIfNotNullAndSetNull(requestData->timeTag);
173     WebNewStringObjFromStringIncr(requestData->timeTag, "t");
174 
175     WebDecrRefCountIfNotNullAndSetNull(requestData->cmdTag);
176     WebNewStringObjFromStringIncr(requestData->cmdTag, "cmd");
177 
178     WebDecrRefCountIfNotNullAndSetNull(requestData->cmdUrlTimestamp);
179     requestData->cmdUrlTimestamp = Tcl_NewBooleanObj(1);
180     Tcl_IncrRefCount(requestData->cmdUrlTimestamp);
181 #endif
182 
183     requestData->requestIsInitialized = 0;
184     return TCL_OK;
185 }
186 
187 /* ----------------------------------------------------------------------------
188  * removeTempFiles -- remove all temporary files accumulated so far,
189  *   and reset hashtable tmpFnList
190  * ------------------------------------------------------------------------- */
removeTempFiles(Tcl_Interp * interp,RequestData * requestData)191 int removeTempFiles(Tcl_Interp * interp, RequestData * requestData)
192 {
193 
194     HashTableIterator iterator;
195     Tcl_Obj *tclo = NULL;
196 
197     /* --------------------------------------------------------------------------
198      * sanity
199      * ----------------------------------------------------------------------- */
200     if (requestData == NULL)
201 	return TCL_ERROR;
202     if (requestData->tmpFnList == NULL)
203 	return TCL_ERROR;
204 
205     /* --------------------------------------------------------------------------
206      * loop
207      * ----------------------------------------------------------------------- */
208     assignIteratorToHashTable(requestData->tmpFnList, &iterator);
209 
210     while (nextFromHashIterator(&iterator) != TCL_ERROR) {
211 	tclo = (Tcl_Obj *) valueOfCurrent(&iterator);
212 	if (tclo != NULL) {
213 
214 	    if (remove(Tcl_GetString(tclo)) < 0) {
215 	      /* not successful: usually because there is no such file */
216 	      if (Tcl_GetErrno() != ENOENT) {
217 		/* a different reason: create error log */
218 		LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__,
219 			"removeTempFiles", WEBLOG_ERROR,
220 			"Error: ", Tcl_ErrnoMsg(Tcl_GetErrno()),
221 			NULL);
222 	      }
223 	    } else {
224 	      /* log if successfully removed */
225 	      LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__,
226 		      "removeTempFiles", WEBLOG_DEBUG,
227 		      "removing temporary file ", Tcl_GetString(tclo), ".",
228 		      NULL);
229 	    }
230 
231 	    Tcl_DecrRefCount(tclo);
232 	}
233     }
234     return resetHashTable(requestData->tmpFnList, TCL_STRING_KEYS);
235 }
236 
237 
238 /* ----------------------------------------------------------------------------
239  * destroy
240  * ------------------------------------------------------------------------- */
destroyRequestData(ClientData clientData,Tcl_Interp * interp)241 void destroyRequestData(ClientData clientData, Tcl_Interp * interp)
242 {
243 
244     RequestData *requestData = NULL;
245 
246     if (clientData != NULL) {
247 
248 	requestData = (RequestData *) clientData;
249 
250 	/* delete exit handler to prevent memory leak */
251 	Tcl_DeleteExitHandler(destroyRequestDataHook, (ClientData) requestData);
252 
253 	WebDecrRefCountIfNotNull(requestData->cmdTag);
254 	WebDecrRefCountIfNotNull(requestData->timeTag);
255 	WebDecrRefCountIfNotNull(requestData->cmdUrlTimestamp);
256 	destroyParamList(requestData->request);
257 
258 	WebDecrRefCountIfNotNull(requestData->upLoadFileSize);
259 
260 	destroyParamList(requestData->paramList);
261 	destroyParamList(requestData->formVarList);
262 
263 	destroyParamList(requestData->cmdList);
264 
265 	/* ------------------------------------------------------------------------
266 	 * now delete temporary files
267 	 * --------------------------------------------------------------------- */
268 	if (requestData->tmpFnList != NULL) {
269 
270 	    removeTempFiles(interp, requestData);
271 	    /* this time delete the hash */
272 	    HashUtlDelFree(requestData->tmpFnList);
273 	}
274 
275 	destroyParamList(requestData->staticList);
276 
277 	WebFreeIfNotNull(requestData);
278     }
279 }
280 
281 /* ----------------------------------------------------------------------------
282  * Web_Param
283  * ------------------------------------------------------------------------- */
Web_Param(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])284 int Web_Param(ClientData clientData,
285 	      Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
286 {
287 
288     RequestData *requestData = NULL;
289 
290     /* --------------------------------------------------------------------------
291      * check for internal data
292      * ----------------------------------------------------------------------- */
293     WebAssertData(interp, clientData, "Web_Param", TCL_ERROR)
294 	requestData = (RequestData *) clientData;
295 
296     return paramGet((ParamList *) requestData->paramList, interp, objc, objv,
297 		    0);
298 }
299 
300 /* ----------------------------------------------------------------------------
301  * Web_FormVar
302  * ------------------------------------------------------------------------- */
Web_FormVar(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])303 int Web_FormVar(ClientData clientData,
304 		Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
305 {
306 
307     RequestData *requestData = NULL;
308 
309     /* --------------------------------------------------------------------------
310      * check for internal data
311      * ----------------------------------------------------------------------- */
312     WebAssertData(interp, clientData, "Web_FormVar", TCL_ERROR)
313 	requestData = (RequestData *) clientData;
314 
315     return paramGet((ParamList *) requestData->formVarList, interp, objc,
316 		    objv, 0);
317 }
318 
319 
320 
321 /* ----------------------------------------------------------------------------
322  * Web_TempFile -- return a temporary filename
323  * note: websh3 keeps a list of all filenames that have been generated with
324  * note: this command, and will attempt to delete these files at the end
325  * note: or if you call web::tempfile -remove
326  * ------------------------------------------------------------------------- */
Web_TempFile(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])327 int Web_TempFile(ClientData clientData,
328 		 Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
329 {
330 
331     Tcl_Obj *tclo = NULL;
332     RequestData *requestData = NULL;
333     static TCLCONST char *params[] = { "-path", "-prefix", "-remove", NULL };
334     enum params
335     { PATH, PREFIX, REMOVE };
336     int idx = -1;
337 
338     WebAssertData(interp, clientData, "Web_TempFile", TCL_ERROR)
339 	requestData = (RequestData *) clientData;
340 
341     WebAssertArgs(interp, objc, objv, params, idx, -1);
342 
343     /* do we see "-remove" ? */
344     if (argKeyExists(objc, objv, (char *)params[REMOVE]) == TCL_OK) {
345 
346 	/* do remove */
347 	return removeTempFiles(interp, requestData);
348     }
349 
350     /* lazy arg-check - we do not check values of params.
351      * this works beause argValueOfKey may return NULL and
352      * tempFileName takes NULL as "default" */
353     tclo = tempFileName(interp, requestData,
354 			argValueOfKey(objc, objv, (char *)params[PATH]),
355 			argValueOfKey(objc, objv, (char *)params[PREFIX]));
356 
357     if (tclo == NULL)
358 	return TCL_ERROR;
359 
360     Tcl_SetObjResult(interp, tclo);
361     return TCL_OK;
362 }
363 
364 /* ----------------------------------------------------------------------------
365  * tempFileName -- return a temporary filename
366  * note: websh3 keeps a list of all filenames that have been generated with
367  * note: this command, and will attempt to delete these files at the end !
368  * note: If you would like to keep the file, you will have to copy it
369  * note: to a safe place before the end of the websh3 app.
370  * ------------------------------------------------------------------------- */
tempFileName(Tcl_Interp * interp,RequestData * requestData,Tcl_Obj * path,Tcl_Obj * prefix)371 Tcl_Obj *tempFileName(Tcl_Interp * interp, RequestData * requestData,
372 		      Tcl_Obj * path, Tcl_Obj * prefix)
373 {
374 
375     Tcl_Obj *tclo = NULL;
376     char *pathstring = NULL;
377     char *prefixstring = NULL;
378     char *tmpn = NULL;
379     int trycnt = 0;
380     Tcl_Obj *mytime = NULL;
381 
382     if (requestData == NULL)
383 	return NULL;
384 
385     if (path != NULL)
386 	pathstring = Tcl_GetString(path);
387     if (prefix != NULL)
388 	prefixstring = Tcl_GetString(prefix);
389 
390 #ifdef WIN32
391     tmpn = _tempnam(pathstring, prefixstring);
392 #else
393     tmpn = tempnam(pathstring, prefixstring);
394 #endif
395 
396     if (tmpn == NULL) {
397 
398 	LOG_MSG(interp, WRITE_LOG | SET_RESULT,
399 		__FILE__, __LINE__,
400 		"web::tempfile", WEBLOG_ERROR,
401 		"error requesting unique filename", NULL);
402 	return NULL;
403     }
404 
405     tclo = Tcl_NewStringObj(tmpn, -1);
406     Tcl_IncrRefCount(tclo);
407 
408 #ifndef WIN32
409     free(tmpn);
410 #endif
411 
412     /* now try to add to hash list */
413 
414     while ((appendToHashTable(requestData->tmpFnList,
415 			      Tcl_GetString(tclo),
416 			      (void *) tclo) == TCL_ERROR)
417 	   && (trycnt++ < 100)) {
418 
419 	mytime = Tcl_NewLongObj(((unsigned long) clock()) % 1000);
420 	Tcl_IncrRefCount(mytime);
421 	Tcl_AppendObjToObj(tclo, mytime);
422 	Tcl_DecrRefCount(mytime);
423     }
424 
425     if (trycnt >= 100) {
426 
427 	LOG_MSG(interp, WRITE_LOG | SET_RESULT,
428 		__FILE__, __LINE__,
429 		"web::tempfile", WEBLOG_ERROR,
430 		"error adding \"", Tcl_GetString(tclo),
431 		"\" to internal list of files", NULL);
432 
433 	removeFromHashTable(requestData->tmpFnList, Tcl_GetString(tclo));
434 	Tcl_DecrRefCount(tclo);
435 	return NULL;
436     }
437 
438     /* fixme-later: should I check for for TMP_MAX filenames per app ?
439     */
440     return tclo;
441 }
442 
443 /* --------------------------------------------------------------------------
444  * accessor to request object
445  * ------------------------------------------------------------------------*/
Web_Request(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])446 int Web_Request(ClientData clientData,
447 		Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[])
448 {
449 
450     static char *params[] = { "-reset", "-channel", NULL };
451     enum params
452     { REQUESTRESET, DEFAULTCHANNELNAME };
453 
454     int res;
455     RequestData *requestData;
456 
457     /* --------------------------------------------------------------------------
458      * sanity
459      * ----------------------------------------------------------------------- */
460     WebAssertData(interp, clientData, "web::request", TCL_ERROR);
461     requestData = (RequestData *) clientData;
462     WebAssertData(interp, requestData->request, "web::request", TCL_ERROR);
463 
464     /* make sure we have values */
465     if (requestFillRequestValues(interp, requestData) == TCL_ERROR)
466 	return TCL_ERROR;
467 
468     res = paramGet((ParamList *) requestData->request, interp, objc, objv, 1);
469 
470     if (res == TCL_CONTINUE) {
471 	int opt;
472 	WebAssertObjc(objc != 2, 1, "args ....");
473 	if (paramGetIndexFromObj
474 	    (interp, objv[1], params, "subcommand", 0, &opt) == TCL_ERROR)
475 	    return TCL_ERROR;
476 
477 	switch ((enum params) opt) {
478 
479 	case DEFAULTCHANNELNAME:
480 	    Tcl_SetObjResult(interp, requestGetDefaultChannelName(interp));
481 	    return TCL_OK;
482 	    break;
483 	case REQUESTRESET:
484 	    return resetRequestData(interp, requestData);
485 	    break;
486 	default:
487 	    break;
488 	}
489     }
490     return TCL_OK;
491 }
492