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