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