Annotation of parser3/src/classes/op.C, revision 1.21
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.21 ! parser 8: $Id: op.C,v 1.20 2001/05/17 12:51:05 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:
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.19 parser 269: Table *protocol2driver_and_client=
270: static_cast<Table *>(r.classes_conf.get(OP->name()));
1.11 paf 271:
1.1 paf 272: // connect
273: SQL_Connection& connection=SQL_driver_manager->get_connection(
1.19 parser 274: url.as_string(), protocol2driver_and_client);
1.1 paf 275:
276: Exception rethrow_me;
277: // remember/set current connection
278: SQL_Connection *saved_connection=r.connection;
279: r.connection=&connection;
280: // execute body
281: bool body_failed=false;
282: PTRY
283: r.write_assign_lang(r.process(body_code));
284: PCATCH(e) { // connect/process problem
285: rethrow_me=e; body_failed=true;
286: }
287: PEND_CATCH
288:
289: bool finalizer_failed=false;
290: PTRY
291: // FINALLY
292: if(body_failed)
293: connection.rollback();
294: else
295: connection.commit();
296: PCATCH(e) { // commit/rollback problem
297: rethrow_me=e; finalizer_failed=true;
298: }
299: PEND_CATCH
300:
301: // close connection [cache it]
1.21 ! parser 302: connection.close();
1.1 paf 303: // recall current connection from remembered
304: r.connection=saved_connection;
305:
306: if(body_failed || finalizer_failed) // were there an exception for us to rethrow?
307: PTHROW(rethrow_me.type(), rethrow_me.code(),
308: rethrow_me.problem_source(),
309: rethrow_me.comment());
310: }
311:
1.10 paf 312: // constructor
313:
1.11 paf 314: MOP::MOP(Pool& apool) : Methoded(apool),
315: main_sql_name(apool, MAIN_SQL_NAME),
316: main_sql_drivers_name(apool, MAIN_SQL_DRIVERS_NAME)
317: {
1.10 paf 318: set_name(*NEW String(pool(), OP_CLASS_NAME));
1.1 paf 319:
320: // ^if(condition){code-when-true}
321: // ^if(condition){code-when-true}{code-when-false}
1.10 paf 322: add_native_method("if", Method::CT_ANY, _if, 2, 3);
1.1 paf 323:
324: // ^untaint[as-is|uri|sql|js|html|html-typo]{code}
1.10 paf 325: add_native_method("untaint", Method::CT_ANY, _untaint, 2, 2);
1.1 paf 326:
327: // ^taint[as-is|uri|sql|js|html|html-typo]{code}
1.10 paf 328: add_native_method("taint", Method::CT_ANY, _taint, 1, 2);
1.1 paf 329:
330: // ^process[code]
1.10 paf 331: add_native_method("process", Method::CT_ANY, _process, 1, 1);
1.1 paf 332:
333: // ^rem{code}
1.10 paf 334: add_native_method("rem", Method::CT_ANY, _rem, 1, 1);
1.1 paf 335:
336: // ^while(condition){code}
1.10 paf 337: add_native_method("while", Method::CT_ANY, _while, 2, 2);
1.1 paf 338:
339: // ^use[file]
1.10 paf 340: add_native_method("use", Method::CT_ANY, _use, 1, 1);
1.1 paf 341:
342: // ^for[i;from-number;to-number-inclusive]{code}[delim]
1.10 paf 343: add_native_method("for", Method::CT_ANY, _for, 3+1, 3+1+1);
1.1 paf 344:
345: // ^eval(expr)
346: // ^eval(expr)[format]
1.10 paf 347: add_native_method("eval", Method::CT_ANY, _eval, 1, 2);
1.1 paf 348:
349:
350: // math functions
351:
352: // ^round(expr)
1.10 paf 353: add_native_method("round", Method::CT_ANY, _round, 1, 1);
1.1 paf 354:
355: // ^floor(expr)
1.10 paf 356: add_native_method("floor", Method::CT_ANY, _floor, 1, 1);
1.1 paf 357:
358: // ^ceiling(expr)
1.10 paf 359: add_native_method("ceiling", Method::CT_ANY, _ceiling, 1, 1);
1.1 paf 360:
361: // ^abs(expr)
1.10 paf 362: add_native_method("abs", Method::CT_ANY, _abs, 1, 1);
1.1 paf 363:
364: // ^sign(expr)
1.10 paf 365: add_native_method("sign", Method::CT_ANY, _sign, 1, 1);
1.1 paf 366:
367:
368: // connect
369:
370: // ^connect[protocol://user:pass@host[:port]/database]{code with ^sql-s}
1.10 paf 371: add_native_method("connect", Method::CT_ANY, _connect, 2, 2);
372:
373: }
1.11 paf 374:
375: // constructor & configurator
1.1 paf 376:
1.10 paf 377: Methoded *MOP_create(Pool& pool) {
1.11 paf 378: return OP=new(pool) MOP(pool);
379: }
380:
381:
382: void MOP::configure_user(Request& r) {
383: Pool& pool=r.pool();
384:
385: // $MAIN:SQL.drivers
386: if(Value *sql=r.main_class->get_element(main_sql_name))
387: if(Value *element=sql->get_element(main_sql_drivers_name))
388: if(Table *protocol2library=element->get_table())
389: r.classes_conf.put(name(), protocol2library);
1.1 paf 390: }
E-mail: