Annotation of parser3/src/classes/op.C, revision 1.18
1.1 paf 1: /** @file
1.9 paf 2: Parser: parser @b operators.
1.1 paf 3:
4: Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)
7:
1.18 ! parser 8: $Id: op.C,v 1.17 2001/05/08 06:13:00 paf Exp $
1.1 paf 9: */
10:
1.13 paf 11: #include "classes.h"
1.1 paf 12: #include "pa_config_includes.h"
13: #include "pa_common.h"
14: #include "pa_request.h"
15: #include "pa_vint.h"
16: #include "pa_sql_connection.h"
17:
1.18 ! parser 18: // limits
! 19:
! 20: #define MAX_LOOPS 10000
! 21:
1.10 paf 22: // defines
23:
24: #define OP_CLASS_NAME "OP"
25:
1.11 paf 26: #define MAIN_SQL_NAME "SQL"
27: #define MAIN_SQL_DRIVERS_NAME "drivers"
28:
29: // local variable
30:
31: static Methoded *OP;
32:
1.10 paf 33: // class
34:
35: class MOP : public Methoded {
36: public:
37: MOP(Pool& pool);
1.11 paf 38: public: // Methoded
1.10 paf 39: bool used_directly() { return true; }
1.11 paf 40: void configure_user(Request& r);
41: private:
42: String main_sql_name;
43: String main_sql_drivers_name;
1.10 paf 44: };
45:
46: // methods
47:
1.5 paf 48: static void _if(Request& r, const String&, MethodParams *params) {
49: Value& condition_code=params->get(0);
1.1 paf 50:
51: bool condition=r.process(condition_code,
52: 0/*no name*/,
53: false/*don't intercept string*/).as_bool();
1.6 paf 54: if(condition)
1.5 paf 55: r.write_pass_lang(r.process(params->get(1)));
1.6 paf 56: else if(params->size()==3)
1.5 paf 57: r.write_pass_lang(r.process(params->get(2)));
1.1 paf 58: }
59:
1.5 paf 60: static void _untaint(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 61: Pool& pool=r.pool();
62:
1.5 paf 63: const String& lang_name=r.process(params->get(0)).as_string();
1.1 paf 64: String::Untaint_lang lang=static_cast<String::Untaint_lang>(
65: untaint_lang_name2enum->get_int(lang_name));
66: if(!lang)
67: PTHROW(0, 0,
68: &lang_name,
69: "invalid untaint language");
70:
71: {
1.5 paf 72: Value& vbody=params->get_junction(1, "body must be code");
1.1 paf 73:
74: Temp_lang temp_lang(r, lang); // set temporarily specified ^untaint[language;
1.5 paf 75: r.write_pass_lang(r.process(vbody)); // process marking tainted with that lang
1.1 paf 76: }
77: }
78:
1.5 paf 79: static void _taint(Request& r, const String&, MethodParams *params) {
1.1 paf 80: Pool& pool=r.pool();
81:
82: String::Untaint_lang lang;
83: if(params->size()==1)
84: lang=String::UL_TAINTED; // mark as simply 'tainted'. useful in table:set
85: else {
1.3 paf 86: const String& lang_name=
1.5 paf 87: r.process(params->get(0)).as_string();
1.1 paf 88: lang=static_cast<String::Untaint_lang>(
89: untaint_lang_name2enum->get_int(lang_name));
90: if(!lang)
91: PTHROW(0, 0,
1.3 paf 92: &lang_name,
93: "invalid taint language");
1.1 paf 94: }
95:
96: {
1.5 paf 97: Value& vbody=params->get_no_junction(params->size()-1, "body must not be code");
1.1 paf 98:
99: String result(r.pool());
100: result.append(
101: vbody.as_string(), // process marking tainted with that lang
102: lang, true); // force result language to specified
103: r.write_pass_lang(result);
104: }
105: }
106:
1.5 paf 107: static void _process(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 108: // calculate pseudo file name of processed chars
109: // would be something like "/some/file(4) process"
110: char place[MAX_STRING];
111: #ifndef NO_STRING_ORIGIN
112: const Origin& origin=method_name.origin();
113: snprintf(place, MAX_STRING, "%s(%d) %s",
114: origin.file, 1+origin.line,
115: method_name.cstr());
116: #else
117: strncpy(place, MAX_STRING, method_name.cstr());
118: #endif
119:
120: VStateless_class& self_class=*r.self->get_class();
121: {
122: // temporary zero @main so to maybe-replace it in processed code
123: Temp_method temp_method_main(self_class, *main_method_name, 0);
124: // temporary zero @auto so it wouldn't be auto-called in Request::use_buf
125: Temp_method temp_method_auto(self_class, *auto_method_name, 0);
126:
127: // evaluate source to process
128: const String& source=
1.5 paf 129: r.process(params->get(0)).as_string();
1.1 paf 130:
131: // process source code, append processed methods to 'self' class
132: // maybe-define new @main
133: r.use_buf(source.cstr(), place, &self_class);
134:
135: // maybe-execute @main[]
136: if(const Method *method=self_class.get_method(*main_method_name)) {
137: // execute!
138: r.execute(*method->parser_code);
139: }
140: }
141: }
142:
1.5 paf 143: static void _rem(Request& r, const String&, MethodParams *params) {
144: params->get_junction(0, "body must be code");
1.1 paf 145: }
146:
1.5 paf 147: static void _while(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 148: Pool& pool=r.pool();
149:
1.5 paf 150: Value& vcondition=params->get_junction(0, "condition must be expression");
151: Value& body=params->get_junction(1, "body must be code");
1.1 paf 152:
153: // while...
154: int endless_loop_count=0;
155: while(true) {
1.18 ! parser 156: if(++endless_loop_count>=MAX_LOOPS) // endless loop?
1.1 paf 157: PTHROW(0, 0,
158: &method_name,
159: "endless loop detected");
160:
161: bool condition=
162: r.process(
163: vcondition,
164: 0/*no name*/,
165: false/*don't intercept string*/).as_bool();
166: if(!condition) // ...condition is true
167: break;
168:
169: // write processed body
170: r.write_pass_lang(r.process(body));
171: }
172: }
173:
1.5 paf 174: static void _use(Request& r, const String& method_name, MethodParams *params) {
175: Value& vfile=params->get_no_junction(0, "file name must not be code");
1.1 paf 176: r.use_file(r.absolute(vfile.as_string()));
177: }
178:
1.5 paf 179: static void _for(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 180: Pool& pool=r.pool();
1.5 paf 181: const String& var_name=r.process(params->get(0)).as_string();
1.18 ! parser 182: int from=r.process(params->get(1)).as_int();
! 183: int to=r.process(params->get(2)).as_int();
1.5 paf 184: Value& body_code=params->get_junction(3, "body must be code");
185: Value *delim_code=params->size()==3+1+1?¶ms->get(3+1):0;
1.1 paf 186:
187: bool need_delim=false;
188: VInt *vint=new(pool) VInt(pool, 0);
189: int endless_loop_count=0;
190: for(int i=from; i<=to; i++) {
1.18 ! parser 191: if(++endless_loop_count>=MAX_LOOPS) // endless loop?
1.1 paf 192: PTHROW(0, 0,
193: &method_name,
194: "endless loop detected");
195: vint->set_int(i);
1.4 paf 196: r.root->put_element(var_name, vint);
1.1 paf 197:
198: Value& processed_body=r.process(body_code);
199: if(delim_code) { // delimiter set?
200: const String *string=processed_body.get_string();
201: if(need_delim && string && string->size()) // need delim & iteration produced string?
202: r.write_pass_lang(r.process(*delim_code));
203: need_delim=true;
204: }
205: r.write_pass_lang(processed_body);
206: }
207: }
208:
1.15 paf 209: static void _eval(Request& r, const String& method_name, MethodParams *params) {
1.5 paf 210: Value& expr=params->get_junction(0, "need expression");
1.1 paf 211: // evaluate expresion
212: Value *result=r.process(expr,
1.16 paf 213: 0/*no name YET*/,
1.1 paf 214: true/*don't intercept string*/).as_expr_result();
215: if(params->size()==2) {
1.5 paf 216: Value& fmt=params->get_no_junction(1, "fmt must not be code");
1.1 paf 217:
218: Pool& pool=r.pool();
219: String& string=*new(pool) String(pool);
220: string.APPEND_CONST(format(pool, result->as_double(), fmt.as_string().cstr()));
221: result=new(pool) VString(string);
222: }
1.16 paf 223: result->set_name(method_name);
1.1 paf 224: r.write_no_lang(*result);
225: }
226:
227:
228: typedef double (*math_one_double_op_func_ptr)(double);
229: static double round(double op) { return floor(op+0.5); }
230: static double sign(double op) { return op > 0 ? 1 : ( op < 0 ? -1 : 0 ); }
231:
1.17 paf 232: static void double_one_op(Request& r,
233: const String& method_name, MethodParams *params,
234: math_one_double_op_func_ptr func) {
1.1 paf 235: Pool& pool=r.pool();
1.5 paf 236: Value& param=params->get_junction(0, "parameter must be expression");
1.1 paf 237:
238: Value& result=*new(pool) VDouble(pool, (*func)(r.process(param).as_double()));
1.16 paf 239: result.set_name(method_name);
1.1 paf 240: r.write_no_lang(result);
241: }
242:
1.5 paf 243: static void _round(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 244: double_one_op(r, method_name, params, &round);
245: }
246:
1.5 paf 247: static void _floor(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 248: double_one_op(r, method_name, params, &floor);
249: }
250:
1.5 paf 251: static void _ceiling(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 252: double_one_op(r, method_name, params, &ceil);
253: }
254:
1.5 paf 255: static void _abs(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 256: double_one_op(r, method_name, params, &fabs);
257: }
258:
1.5 paf 259: static void _sign(Request& r, const String& method_name, MethodParams *params) {
1.1 paf 260: double_one_op(r, method_name, params, &sign);
261: }
262:
1.5 paf 263: static void _connect(Request& r, const String&, MethodParams *params) {
1.1 paf 264: Pool& pool=r.pool();
265:
1.5 paf 266: Value& url=params->get_no_junction(0, "url must not be code");
267: Value& body_code=params->get_junction(1, "body must be code");
1.1 paf 268:
1.11 paf 269: Table *protocol2library=static_cast<Table *>(r.classes_conf.get(OP->name()));
270:
1.1 paf 271: // connect
272: SQL_Connection& connection=SQL_driver_manager->get_connection(
1.11 paf 273: url.as_string(), protocol2library);
1.1 paf 274:
275: Exception rethrow_me;
276: // remember/set current connection
277: SQL_Connection *saved_connection=r.connection;
278: r.connection=&connection;
279: // execute body
280: bool body_failed=false;
281: PTRY
282: r.write_assign_lang(r.process(body_code));
283: PCATCH(e) { // connect/process problem
284: rethrow_me=e; body_failed=true;
285: }
286: PEND_CATCH
287:
288: bool finalizer_failed=false;
289: PTRY
290: // FINALLY
291: if(body_failed)
292: connection.rollback();
293: else
294: connection.commit();
295: PCATCH(e) { // commit/rollback problem
296: rethrow_me=e; finalizer_failed=true;
297: }
298: PEND_CATCH
299:
300: // close connection [cache it]
301: connection.close();
302: // recall current connection from remembered
303: r.connection=saved_connection;
304:
305: if(body_failed || finalizer_failed) // were there an exception for us to rethrow?
306: PTHROW(rethrow_me.type(), rethrow_me.code(),
307: rethrow_me.problem_source(),
308: rethrow_me.comment());
309: }
310:
1.10 paf 311: // constructor
312:
1.11 paf 313: MOP::MOP(Pool& apool) : Methoded(apool),
314: main_sql_name(apool, MAIN_SQL_NAME),
315: main_sql_drivers_name(apool, MAIN_SQL_DRIVERS_NAME)
316: {
1.10 paf 317: set_name(*NEW String(pool(), OP_CLASS_NAME));
1.1 paf 318:
319: // ^if(condition){code-when-true}
320: // ^if(condition){code-when-true}{code-when-false}
1.10 paf 321: add_native_method("if", Method::CT_ANY, _if, 2, 3);
1.1 paf 322:
323: // ^untaint[as-is|uri|sql|js|html|html-typo]{code}
1.10 paf 324: add_native_method("untaint", Method::CT_ANY, _untaint, 2, 2);
1.1 paf 325:
326: // ^taint[as-is|uri|sql|js|html|html-typo]{code}
1.10 paf 327: add_native_method("taint", Method::CT_ANY, _taint, 1, 2);
1.1 paf 328:
329: // ^process[code]
1.10 paf 330: add_native_method("process", Method::CT_ANY, _process, 1, 1);
1.1 paf 331:
332: // ^rem{code}
1.10 paf 333: add_native_method("rem", Method::CT_ANY, _rem, 1, 1);
1.1 paf 334:
335: // ^while(condition){code}
1.10 paf 336: add_native_method("while", Method::CT_ANY, _while, 2, 2);
1.1 paf 337:
338: // ^use[file]
1.10 paf 339: add_native_method("use", Method::CT_ANY, _use, 1, 1);
1.1 paf 340:
341: // ^for[i;from-number;to-number-inclusive]{code}[delim]
1.10 paf 342: add_native_method("for", Method::CT_ANY, _for, 3+1, 3+1+1);
1.1 paf 343:
344: // ^eval(expr)
345: // ^eval(expr)[format]
1.10 paf 346: add_native_method("eval", Method::CT_ANY, _eval, 1, 2);
1.1 paf 347:
348:
349: // math functions
350:
351: // ^round(expr)
1.10 paf 352: add_native_method("round", Method::CT_ANY, _round, 1, 1);
1.1 paf 353:
354: // ^floor(expr)
1.10 paf 355: add_native_method("floor", Method::CT_ANY, _floor, 1, 1);
1.1 paf 356:
357: // ^ceiling(expr)
1.10 paf 358: add_native_method("ceiling", Method::CT_ANY, _ceiling, 1, 1);
1.1 paf 359:
360: // ^abs(expr)
1.10 paf 361: add_native_method("abs", Method::CT_ANY, _abs, 1, 1);
1.1 paf 362:
363: // ^sign(expr)
1.10 paf 364: add_native_method("sign", Method::CT_ANY, _sign, 1, 1);
1.1 paf 365:
366:
367: // connect
368:
369: // ^connect[protocol://user:pass@host[:port]/database]{code with ^sql-s}
1.10 paf 370: add_native_method("connect", Method::CT_ANY, _connect, 2, 2);
371:
372: }
1.11 paf 373:
374: // constructor & configurator
1.1 paf 375:
1.10 paf 376: Methoded *MOP_create(Pool& pool) {
1.11 paf 377: return OP=new(pool) MOP(pool);
378: }
379:
380:
381: void MOP::configure_user(Request& r) {
382: Pool& pool=r.pool();
383:
384: // $MAIN:SQL.drivers
385: if(Value *sql=r.main_class->get_element(main_sql_name))
386: if(Value *element=sql->get_element(main_sql_drivers_name))
387: if(Table *protocol2library=element->get_table())
388: r.classes_conf.put(name(), protocol2library);
1.1 paf 389: }
E-mail: