PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/WtWebErrors.c

http://aptcl.googlecode.com/
C | 541 lines | 403 code | 101 blank | 37 comment | 124 complexity | 800b265790b0114e56177ae2ddcfa06c MD5 | raw file
  1. /*
  2. * Copyright 2000 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "WtWebErrors.h"
  17. #include "WtUtil.h"
  18. #include "WtContextEvents.h"
  19. #include "WtTable.h"
  20. #include "WtTableUtil.h"
  21. #include "WtResponse.h"
  22. /* Handle the errors collected during a request */
  23. int WtHandleErrors(WtContext *w, Tcl_Interp *interp, request_rec *apReq,
  24. int useScripts, int *sendStatus)
  25. {
  26. int ok = 1, len, done = 0;
  27. Tcl_Obj *handlers, *handler, *destString;
  28. if (!interp && w->web) {
  29. interp = w->web->interp;
  30. }
  31. if (interp && useScripts) {
  32. /* Try the user-defined error handlers */
  33. handlers = WtTableGetObjFromStr(w->web->ctxEventHandlers, "error", NULL);
  34. if (handlers) {
  35. if (Tcl_ListObjLength(interp, handlers, &len) != TCL_OK) {
  36. ok = 0;
  37. } else if (len) {
  38. if (!WtEvalEventHandlers(&w->web->ctxEventHandlers, "error",
  39. interp, w)) {
  40. WtInterpError(HERE, w, interp);
  41. ok = 0;
  42. } else {
  43. done = 1;
  44. }
  45. }
  46. }
  47. /* Try the default error handler script */
  48. if (!done) {
  49. if (Tcl_PkgRequire(interp, "wt::server", NULL, 0) == NULL) {
  50. WtInterpError(HERE, w, interp);
  51. ok = 0;
  52. } else {
  53. handler = WtNewString("wt::server::handleErrors");
  54. Tcl_IncrRefCount(handler);
  55. if (WtEvalIncr(interp, 1, &handler, TCL_EVAL_DIRECT) != TCL_OK) {
  56. WtInterpError(HERE, w, interp);
  57. ok = 0;
  58. } else {
  59. done = 1;
  60. }
  61. Tcl_DecrRefCount(handler);
  62. }
  63. }
  64. }
  65. /* If the scripts failed, use the error function below */
  66. if (!done) {
  67. destString = WtNewString(NULL);
  68. Tcl_IncrRefCount(destString);
  69. if (!WtPrintErrorNotices(w, destString, interp)) {
  70. WtInterpError(HERE, w, interp);
  71. ok = 0;
  72. }
  73. if (!WtWriteResponse(WtToString(destString),
  74. Tcl_GetCharLength(destString), w)) {
  75. ok = 0;
  76. } else {
  77. done = 1;
  78. }
  79. Tcl_DecrRefCount(destString);
  80. }
  81. *sendStatus = done;
  82. return ok;
  83. }
  84. /* Create an HTML error page */
  85. int WtPrintError(const char *msg, Tcl_Obj *destString)
  86. {
  87. int ok = 1, len;
  88. Tcl_AppendToObj(destString,
  89. "<html>\n<head>\n<title>Wtcl Error</title>\n</head>\n<body>\n", -1);
  90. Tcl_AppendToObj(destString, "<h1>Error</h1>\n", -1);
  91. if (msg && (len = strlen(msg))) {
  92. Tcl_AppendToObj(destString,
  93. "<p>Sorry, an error occurred while processing this request:</p>\n", -1);
  94. Tcl_AppendToObj(destString, "<p><ul><pre>", -1);
  95. Tcl_AppendToObj(destString, msg, len);
  96. Tcl_AppendToObj(destString, "</pre></ul></p>", -1);
  97. } else {
  98. Tcl_AppendToObj(destString,
  99. "<p>Sorry, an error occurred while processing this request.</p>\n", -1);
  100. }
  101. Tcl_AppendToObj(destString, "</body>\n</html>\n", -1);
  102. return ok;
  103. }
  104. /* Create an HTML string containing the error notices. This
  105. function is used if the interp is invalid. Otherwise,
  106. the error handler script is evaluated. */
  107. int WtPrintErrorNotices(WtContext *w, Tcl_Obj *destString, Tcl_Interp *interp)
  108. {
  109. int ok = 1, i, len, level;
  110. Tcl_Obj *errors, *item, *levelObj, *err;
  111. errors = Tcl_NewStringObj(NULL, 0);
  112. Tcl_IncrRefCount(errors);
  113. if (interp && w->web && w->web->notices) {
  114. if (Tcl_ListObjLength(interp, w->web->notices, &len) != TCL_OK) {
  115. ok = 0;
  116. } else {
  117. for (i = 0; i < len; i++) {
  118. if (Tcl_ListObjIndex(interp, w->web->notices, i, &item) != TCL_OK) {
  119. ok = 0;
  120. break;
  121. } else if (Tcl_ListObjIndex(interp, item, 0, &levelObj) != TCL_OK ||
  122. Tcl_ListObjIndex(interp, item, 1, &err) != TCL_OK) {
  123. ok = 0;
  124. } else if (Tcl_GetIntFromObj(interp, levelObj, &level) != TCL_OK) {
  125. ok = 0;
  126. } else if (level <= APLOG_ERR) {
  127. Tcl_AppendToObj(errors, "<p>", 3);
  128. Tcl_AppendObjToObj(errors, err);
  129. Tcl_AppendToObj(errors, "</p>", 4);
  130. }
  131. }
  132. }
  133. }
  134. if (ok) {
  135. if (!WtPrintError(WtToString(errors), destString)) {
  136. ok = 0;
  137. }
  138. } else {
  139. WtInterpError(HERE, w, interp);
  140. err = Tcl_GetVar2Ex(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
  141. if (!err || !Tcl_GetCharLength(err)) {
  142. err = Tcl_GetObjResult(interp);
  143. }
  144. if (!WtPrintError((err ? WtToString(err) : NULL), destString)) {
  145. ok = 0;
  146. }
  147. }
  148. Tcl_DecrRefCount(errors);
  149. return ok;
  150. }
  151. /* Create an error page using a format string */
  152. int WtPrintErrorFmt(char *file, int line, Tcl_Obj *destString,
  153. WtContext *w, request_rec *apReq, const char *fmt, ...)
  154. {
  155. va_list args;
  156. int ok = 1;
  157. char buf[MAX_STRING_LEN];
  158. va_start(args, fmt);
  159. ap_vsnprintf(buf, sizeof(buf), fmt, args);
  160. va_end(args);
  161. ok = WtPrintError(buf, destString);
  162. return ok;
  163. }
  164. /* Print an initialization error before the interp is ready */
  165. int WtPrintInitError(char *file, int line, int mask,
  166. request_rec *apReq, int sendHeadersAndFlush, const char *fmt, ...)
  167. {
  168. va_list args;
  169. int ok = 1;
  170. char buf[MAX_STRING_LEN];
  171. Tcl_Obj *logString, *clientString;
  172. buf[0] = '\0';
  173. va_start(args, fmt);
  174. if (fmt) {
  175. ap_vsnprintf(buf, sizeof(buf), fmt, args);
  176. }
  177. va_end(args);
  178. /* Log the error */
  179. logString = WtNewString("Wtcl: ");
  180. Tcl_IncrRefCount(logString);
  181. Tcl_AppendToObj(logString, buf, -1);
  182. ap_log_rerror(APLOG_MARK, mask, apReq, "%s", WtToString(logString));
  183. Tcl_DecrRefCount(logString);
  184. /* Write the error to the client */
  185. clientString = WtNewString(NULL);
  186. Tcl_IncrRefCount(clientString);
  187. WtPrintError(buf, clientString);
  188. if (sendHeadersAndFlush) {
  189. WtSendApHeaders(apReq);
  190. }
  191. ap_rwrite(WtToString(clientString), Tcl_GetCharLength(clientString),
  192. apReq);
  193. if (sendHeadersAndFlush) {
  194. ap_rflush(apReq);
  195. }
  196. Tcl_DecrRefCount(clientString);
  197. return ok;
  198. }
  199. /* Log an interpreter error */
  200. int WtInterpError(const char *file, int line, WtContext *w,
  201. Tcl_Interp *interp)
  202. {
  203. int ok = 1;
  204. Tcl_Obj *err = NULL;
  205. if (interp) {
  206. err = Tcl_GetVar2Ex(interp, "errorInfo", NULL,
  207. TCL_GLOBAL_ONLY);
  208. if (!Tcl_GetCharLength(err)) {
  209. err = Tcl_GetObjResult(interp);
  210. }
  211. }
  212. if (!err) {
  213. err = WtNewString(NULL);
  214. }
  215. Tcl_IncrRefCount(err);
  216. ok = WtLog(file, line, APLOG_ERR | APLOG_NOERRNO, w, "%s",
  217. WtSafeStr(WtToString(err)));
  218. Tcl_DecrRefCount(err);
  219. return ok;
  220. }
  221. /* Evaluate the error handler inside the task namespace */
  222. int WtHandleErrorsNs(WtContext *w)
  223. {
  224. int ok = 1, sendStatus, boolVal;
  225. Tcl_Obj *objv[4];
  226. if (!w->web->interp) {
  227. if (!WtHandleErrors(w, NULL, w->web->apReq, 0, &sendStatus)) {
  228. ok = 0;
  229. }
  230. return ok;
  231. }
  232. if (!Tcl_CreateObjCommand(w->web->interp,
  233. "::wt::internal::handleErrors", WtHandleErrorsCmd,
  234. NULL, NULL)) {
  235. WtInterpError(HERE, w, w->web->interp);
  236. ok = 0;
  237. } else {
  238. objv[0] = WtNewString("namespace");
  239. objv[1] = WtNewString("eval");
  240. objv[2] = w->web->taskNamespace;
  241. objv[3] = WtNewString("::wt::internal::handleErrors");
  242. if (WtEvalIncr(w->web->interp, 4, objv, TCL_EVAL_DIRECT) != TCL_OK ||
  243. !WtGetBoolResult(w->web->interp, &boolVal)) {
  244. WtInterpError(HERE, w, w->web->interp);
  245. ok = 0;
  246. } else if (!boolVal) {
  247. ok = 0;
  248. }
  249. }
  250. return ok;
  251. }
  252. /* Internal command to print the request errors */
  253. int WtHandleErrorsCmd(ClientData clientData, Tcl_Interp *interp,
  254. int objc, Tcl_Obj *const objv[])
  255. {
  256. int ok = 1, sendStatus = 0;
  257. WtContext *w = WtGetAssocContext(interp);
  258. if (Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])) != 0) {
  259. WtInterpError(HERE, w, interp);
  260. ok = 0;
  261. } else {
  262. ok = WtHandleErrors(w, interp, w->web->apReq, 1, &sendStatus);
  263. }
  264. Tcl_SetObjResult(interp, WtNewBool(ok));
  265. return TCL_OK;
  266. }
  267. /* Start collecting command errors */
  268. int WtStartErrors(WtContext *w, Tcl_Interp *interp, int *errorSetId)
  269. {
  270. int ok = 1, len;
  271. Tcl_Obj *list;
  272. *errorSetId = -1;
  273. if (!w->web->errorStack) {
  274. w->web->errorStack = Tcl_NewListObj(0, NULL);
  275. Tcl_IncrRefCount(w->web->errorStack);
  276. }
  277. list = Tcl_NewListObj(0, NULL);
  278. Tcl_IncrRefCount(list);
  279. if (Tcl_ListObjAppendElement(interp, w->web->errorStack, list) != TCL_OK) {
  280. ok = 0;
  281. } else if (Tcl_ListObjLength(interp, w->web->errorStack, &len) != TCL_OK) {
  282. ok = 0;
  283. } else {
  284. *errorSetId = len - 1;
  285. }
  286. Tcl_DecrRefCount(list);
  287. return ok;
  288. }
  289. /* Stop collecting command errors */
  290. int WtStopErrors(WtContext *w, Tcl_Interp *interp, int errorSetId,
  291. int setResult)
  292. {
  293. int ok = 1, sets, len, i;
  294. Tcl_Obj *list, *msg = NULL, *item, *errorCode, *errorInfo;
  295. if (!w->web->errorStack) {
  296. Tcl_AppendResult(interp, "Invalid errorStack.", NULL);
  297. return 0;
  298. }
  299. if (Tcl_ListObjLength(interp, w->web->errorStack, &sets) != TCL_OK) {
  300. ok = 0;
  301. } else if (errorSetId < 0 || errorSetId >= sets) {
  302. Tcl_AppendResult(interp, "Invalid error set ID.", NULL);
  303. ok = 0;
  304. } else if (Tcl_ListObjIndex(interp, w->web->errorStack, errorSetId,
  305. &list) != TCL_OK) {
  306. ok = 0;
  307. } if (Tcl_ListObjLength(interp, list, &len) != TCL_OK) {
  308. ok = 0;
  309. } else if (setResult && len) {
  310. msg = WtNewString(NULL);
  311. Tcl_IncrRefCount(msg);
  312. for (i = 0; i < len; i++) {
  313. if (Tcl_ListObjIndex(interp, list, i, &item) != TCL_OK) {
  314. ok = 0;
  315. break;
  316. }
  317. if (!WtConvertToTable(item, interp)) {
  318. ok = 0;
  319. break;
  320. }
  321. if (i == 0 && len == 1) {
  322. errorCode = WtTableGetObjFromStr(item, "errorCode", NULL);
  323. if (!errorCode) {
  324. ok = 0;
  325. Tcl_AppendResult(interp, "Invalid errorCode.", NULL);
  326. break;
  327. }
  328. Tcl_SetObjErrorCode(interp, errorCode);
  329. }
  330. errorInfo = WtTableGetObjFromStr(item, "errorInfo", NULL);
  331. if (!errorInfo) {
  332. ok = 0;
  333. Tcl_AppendResult(interp, "Invalid errorInfo.", NULL);
  334. break;
  335. }
  336. if (i > 0) {
  337. Tcl_AppendToObj(msg, "\n followed by\n", -1);
  338. }
  339. Tcl_AppendObjToObj(msg, errorInfo);
  340. }
  341. if (ok) {
  342. Tcl_ResetResult(interp);
  343. /* Tcl_AddObjErrorInfo(interp, WtToString(msg), -1); */
  344. Tcl_SetObjResult(interp, msg);
  345. }
  346. Tcl_DecrRefCount(msg);
  347. }
  348. if (ok) {
  349. if (Tcl_ListObjReplace(interp, w->web->errorStack,
  350. errorSetId, sets - errorSetId, 0, NULL) != TCL_OK) {
  351. ok = 0;
  352. }
  353. }
  354. return ok ? len : -1;
  355. }
  356. /* Add a Tcl error to the current error list */
  357. int WtAddEvalError(Tcl_Interp *interp, WtContext *w)
  358. {
  359. return WtAddInterpErrorInternal(interp, w, 1);
  360. }
  361. int WtAddInterpError(Tcl_Interp *interp, WtContext *w)
  362. {
  363. return WtAddInterpErrorInternal(interp, w, 0);
  364. }
  365. int WtAddInterpErrorInternal(Tcl_Interp *interp, WtContext *w,
  366. int useErrorInfo)
  367. {
  368. int ok = 1;
  369. Tcl_Obj *errorInfo = NULL, *errorCode = NULL;
  370. /* errorCode */
  371. if (useErrorInfo) {
  372. errorCode = Tcl_GetVar2Ex(interp, "errorCode", NULL, 0);
  373. }
  374. if (!errorCode) {
  375. errorCode = WtNewString(NULL);
  376. }
  377. Tcl_IncrRefCount(errorCode);
  378. /* errorInfo */
  379. if (useErrorInfo) {
  380. errorInfo = Tcl_GetVar2Ex(interp, "errorInfo", NULL, 0);
  381. }
  382. if (!errorInfo || !Tcl_GetCharLength(errorInfo)) {
  383. errorInfo = Tcl_GetObjResult(interp);
  384. }
  385. if (!errorInfo) {
  386. errorInfo = WtNewString(NULL);
  387. }
  388. Tcl_IncrRefCount(errorInfo);
  389. /* Reset result */
  390. Tcl_ResetResult(interp);
  391. if (!WtAddError(errorCode, errorInfo, interp, w)) {
  392. ok = 0;
  393. }
  394. Tcl_DecrRefCount(errorCode);
  395. Tcl_DecrRefCount(errorInfo);
  396. return ok;
  397. }
  398. int WtAddError(Tcl_Obj *errorCode, Tcl_Obj *errorInfo, Tcl_Interp *interp,
  399. WtContext *w)
  400. {
  401. int ok = 1, sets;
  402. Tcl_Obj *list, *item;
  403. if (!w->web->errorStack) {
  404. Tcl_AppendResult(interp, "Invalid list.", NULL);
  405. ok = 0;
  406. } else if (Tcl_ListObjLength(interp, w->web->errorStack, &sets) != TCL_OK) {
  407. ok = 0;
  408. } else if (Tcl_ListObjIndex(interp, w->web->errorStack, sets - 1,
  409. &list) != TCL_OK) {
  410. ok = 0;
  411. } else {
  412. item = WtNewTableObj();
  413. Tcl_IncrRefCount(item);
  414. if (errorCode) {
  415. WtTableSetStrToObj(item, "errorCode",
  416. Tcl_DuplicateObj(errorCode));
  417. } else {
  418. WtTableSetStrToObj(item, "errorCode", WtNewString(NULL));
  419. }
  420. if (errorInfo) {
  421. WtTableSetStrToObj(item, "errorInfo",
  422. Tcl_DuplicateObj(errorInfo));
  423. } else {
  424. WtTableSetStrToObj(item, "errorInfo", WtNewString(NULL));
  425. }
  426. if (Tcl_ListObjAppendElement(interp, list, item) != TCL_OK) {
  427. ok = 0;
  428. }
  429. Tcl_DecrRefCount(item);
  430. }
  431. return ok;
  432. }
  433. int WtDeleteErrors(WtContext *w, Tcl_Interp *interp)
  434. {
  435. if (w->web->errorStack) {
  436. Tcl_DecrRefCount(w->web->errorStack);
  437. w->web->errorStack = NULL;
  438. }
  439. return 1;
  440. }