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