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