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